Guide
Component Lifecycle
Resium brings React's component lifecycle to Cesium. The relationship between Cesium elements and React's lifecycle is as follows:
- Render: Nothing is rendered, as a cesium element is not initialized yet.
- Initalize Cesium element: An Cesium element is initialized, and it is added to its parent if the parent exists.
- Re-render: After Cesium element's initalization, children of the component are rendered. DOM never be rendered except root components (Viewer and CesiumWidget).
- Update: Changed properties of the Cesium element are updated. If "Cesium read-only properties" are changed, the Cesium element will be reinitialized.
- Unmount: The Cesium element is destroyed.
Context and component availability
Some components provide some Cesium elements to children via React's context API. Many components use the closest component's context. If the context does not exists, components are not rendered.
All components except root components have to be mounted inside root components. Root components are Viewer
and CesiumWidget
.
For example:
If Entity
component is mounted under Viewer
component, an Entity object is added to Viewer#entities
.
<Viewer>
<Entity />
</Viewer>
is equivalent to
const viewer = new Cesium.Viewer();
const entity = new Cesium.Entity();
viewer.entities.add(entity);
If Entity
component is mounted under CustomDataSource
component, an Entity object is added to CustomDataSource#entities
. At that time, the CustomDataSource object is added to Viewer#dataSources
. Of course CustomDataSource
component should be under Viewer
component.
<Viewer>
<CustomDataSource>
<Entity />
</CustomDataSource>
</Viewer>
is equivalent to
const viewer = new Cesium.Viewer();
const dataSource = new Cesium.CustomDataSource();
const entity = new Cesium.Entity();
customDataSource.entities.add(entity);
viewer.dataSources.add(dataSource);
Such cases are also in other components. For details, refer to "Availability" in the document of each component.
Component location
Please place each component as close as possible under Viewer or CesiumWidget component to avoid extra rendering. Make component hierarchy shallow as possible.
Exception are:
Entity
>*Graphics
Entity
>EntityDescription
ScreenSpaceEventHandler
>ScreenSpaceEvent
xCollection
>x
CustomDataSource
>Entity
GroundPrimitiveCollection
>GroundPrimitive
e.g.: Scene
> Camera
or Camera
> Entity
is not recommended.
// not recomended
<Viewer>
<Scene>
<Camera>
<Entity />
</Camera>
</Scene>
</Viewer>
// recomended
<Viewer>
<Scene />
<Camera />
<Entity />
</Viewer>
Properties
Each components of resium have 4 kinds of properties as bellow.
Cesium properties
"Cesium property" is a property derived from Cesium. Each properties are correspond to a property of the cesium element. All "cesium properties" are variable, so when they are updated in React component, the corresponding properties will be updated seamlessly.
Cesium read-only properties
"Cesium read-only property" is also a property derived from Cesium, but it is immutable. They are available only when initializing the element.
If "Cesium read-only props" are changed in React component, the Cesium element will be destroyed and reinitialized. It can be a cause of performance deterioration. Please use carefully to avoid changing as much as possible.
For example, imageryProvider
property of ImageryLayer
component is a "Cesium read-only property". So in the following code, ImageryLayer is reinitialized in every rendering time!
const Example = () => (
<Viewer>
<ImageryLayer
imageryProvider={
new ArcGisMapServerImageryProvider({
url: "//services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
})
}
/>
</Viewer>
);
If imageryProvider
property is constant, The following is recommended.
import { Viewer, ImageryLayer } from "resium";
import { ArcGisMapServerImageryProvider } from "cesium";
const imageryProvider = new ArcGisMapServerImageryProvider({
url: "//services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer",
});
const ExampleComponent = () => (
<Viewer>
<ImageryLayer imageryProvider={imageryProvider} />
</Viewer>
);
If imageryProvider
property is variable, use useMemo
hooks:
import { useMemo } from "react";
import { Viewer, ImageryLayer } from "resium";
import { ArcGisMapServerImageryProvider } from "cesium";
const ExampleComponent = ({ url }) => {
const imageryProvider = useMemo(() => new ArcGisMapServerImageryProvider({ url }), [url]);
return (
<Viewer>
<ImageryLayer imageryProvider={imageryProvider} />
</Viewer>
);
};
Cesium events
"Cesium event" is a event propery of Cesium element. It can be used in the same way as normal React component events.
They are renamed according to React's custom. For example, Viewer#trackedEntityChanged
=> onTrackedEntityChange
.
Other properties
Some components have convenient properties that do not exist in the Cesium element. They can be used in the same way as normal React component properties.
Accessing to a raw Cesium element
If you want to access a raw Cesium element, you can use ref
prop of components.
Ref is one of the concepts that comes up in React. To see more information, see the React documentation.
To know what is a Cesium element of each component, refer to "Cesium element" in the document of each component.
Note: cesiumElement
property in the ref object can be undefined
: e.g. when an initialization error is occurred. Please check if it's not undefined
when using cesiumElement
.
Way 1: useRef hooks (function component)
Function component:
import { useEffect, useRef } from "react";
import { Viewer } from "resium";
const ExampleComponent = () => {
const ref = useRef(null);
useEffect(() => {
if (ref.current && ref.current.cesiumElement) {
// ref.current.cesiumElement is Cesium's Viewer
// DO SOMETHING
}
}, []);
return <Viewer ref={ref} />;
};
Function component in TypeScript:
import { useEffect, useRef } from "react";
import { Viewer as CesiumViewer } from "cesium";
import { Viewer, CesiumComponentRef } from "resium";
const ExampleComponent = () => {
const ref = useRef<CesiumComponentRef<CesiumViewer>>(null);
useEffect(() => {
if (ref.current?.cesiumElement) {
// ref.current.cesiumElement is Cesium's Viewer
// DO SOMETHING
}
}, []);
return <Viewer ref={ref} />;
};
Way 2: use a function (class component)
Class component:
import { Component } from "react";
import { Viewer } from "resium";
class ExampleComponent extends Component {
componentDidMount() {
if (this.viewer) {
// this.viewer is Cesium's Viewer
// DO SOMETHING
}
}
render() {
return (
<Viewer
ref={e => {
this.viewer = e ? e.cesiumElement : undefined;
}}
/>
);
}
}
Class component in TypeScript:
import { Component } from "react";
import { Viewer as CesiumViewer } from "cesium";
import { Viewer } from "resium";
class ExampleComponent extends Component {
private viewer: CesiumViewer | undefined;
componentDidMount() {
if (this.viewer) {
// this.viewer is Cesium's Viewer
// DO SOMETHING
}
}
render() {
return (
<Viewer
ref={e => {
this.viewer = e ? e.cesiumElement : undefined;
}}
/>
);
}
}
Way 3: use createRef (class component)
Class component:
import { Component, createRef } from "react";
import { Viewer } from "resium";
class ExampleComponent extends Component {
constructor(props) {
super(props);
this.ref = createRef();
}
componentDidMount() {
if (this.ref.current && this.ref.current.cesiumElement) {
// this.ref.current.cesiumElement is Cesium's Viewer
// DO SOMETHING
}
}
render() {
return <Viewer ref={this.ref} />;
}
}
Class component in TypeScript:
import { Component, createRef } from "react";
import { Viewer as CesiumViewer } from "cesium";
import { Viewer } from "resium";
class ExampleComponent extends Component {
private ref = createRef<CesiumViewer | undefined>();
componentDidMount() {
if (this.ref.current?.cesiumElement) {
// this.ref.current.cesiumElement is Cesium's Viewer
// DO SOMETHING
}
}
render() {
return <Viewer ref={this.ref} />;
}
}
Limitations
- Server side rendering is not supported. Cesium can be rendered only in web browsers.
- React Native is not supported. Resium runs only with
react-dom
, as Cesium depends on APIs of web browsers (DOM, WebGL, Web Worker and so on).