I have written a simple treeview in typescript using react to render the DOM.
Every node has to follow the simple interface.
interface INode {
label: string;
children: INode[];
}
and the tree is then described by:
var tree = {
label: "root",
children: [
{
label: "node 1",
children: [
{
label: "node 1.0",
children: []
},
{
label: "node 1.1",
children: []
}
]
},
{
label: "node 2",
children: []
}
]
};
Very simple. The components are now also very simple. For the Nodes I have:
class Node extends React.Component<INode, void>{
render() {
console.log(`Rendering ${this.props.label}`);
var list = this.props.children.map((node, i) => <li key={i}><Node {...node}/></li>);
return (
<div>
<span>{this.props.label}</span>
{this.props.children.length > 0 ? <ul>{list}</ul> : ""}
</div>
);
}
}
and for the root of the tree I use state instead of props. But after 1 second I refresh the state. But the state itself does not change:
class Root extends React.Component<void, INode>{
constructor() {
super();
this.state = tree;
}
componentWillMount() {
setTimeout(() => {
this.setState((prevState: INode, props: void) => {
console.log(`Updating state of ${this.state.label}`);
return prevState;
});
}, 1000);
}
render() {
var list = this.state.children.map((node, i) => <li key={i}><Node {...node} /></li>);
return (
<div>
<span>{this.state.label}</span>
{this.state.children.length > 0 ? <ul>{list}</ul> : ""}
</div>
);
}
}
To run this app there is simply these lines of code:
ReactDOM.render(
<Root/>,
document.getElementById("app")
);
The rendered html looks as it should. My problem now is when I look at the browser console I see
Rendering node 1
Rendering node 1.0
Rendering node 1.1
Rendering node 2
Updating state of root
Rendering node 1
Rendering node 1.0
Rendering node 1.1
Rendering node 2
The whole tree was rerended (into the virtual DOM) while the value of the state and the props of the children did not change.
Assume the rendering process would be very complex and time consuming. Is there possibility to stop the rerendering of childrens into the virtual DOM?
First off, don't spend time on this unless you see the need for it in your application. Checking against virtual DOM is a pretty cheap operation. If, however, you need to optimize your app, there are a few tactics to achieve this.
There are two broad approaches to doing this:
You can avoid re-rendering using shouldComponentUpdate. To make shouldComponentUpdate
more efficient, you can use frameworks to ensure you only have immutable objects in state, or you can simply make a habit of not mutating objects, but rather creating new ones. See React Immutability Helpers. That will allow you to do a shallow check when comparing nextState and nextProps to current state and props (can be done easily with shallowCompare).
This means only updating state or props if the new values are different from the current values. This approach is not brought forth as often, but I feel it has deserves some merit.
State: Only do setState({ value: newValue })
if newValue !== this.state.value
. This can be achieved in multiple ways, but one way is to actually make the check before you do setState()
.
Props: If non-changes come from props, it's typically because the parent component holds a value (in it's state) that is passed to one of it's other children as a prop. The parent component then needs to re-render itself and all of it's children. If you only want the components that depend on the value to change, you can have the components listen a part of a global app state and inject only what is useful for that component. That way, the value will be passed directly to those components who are interested in it, and no other components will be re-rendered.
(This removes the need to store the state in a common parent component, in the cases where multiple components rely on the same data. In addition to improving efficiency, this often makes it easier to create new components that rely on some part of the existing app state.)
You can maintain a global app state using e.g. Flux, Redux or Baobab, to name a few options.
Another advantage of using a global app state, is that you can implement a check (in the app state handler) to ensure you only update the app state with actual changes (i.e. not setting a value if it's the same as it was before).
Another tactic to reduce re-rendering costs is to make your components smaller. Then, each each data change only applies to a little bit of the DOM, because each component has fewer elements to render.
In my opinion you should always try to do this, as, in addition to the performance aspect, it makes you code more understandable, reusable and maintainable.
Each of the tactics can be used alone or in conjunction, but applying both tactics from the first group is typically not going to show notable improvements over applying just one of them.
I'd recommend starting with making your components smaller, because that gives you other advantages as well, as mentioned above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With