Reputation: 193
I have a Canvas component, which looks approximately like this:
class Canvas extends React.Component{
saveRef = node => {
this._canvas = node;
}
shouldComponentUpdate(){
/*I will never re-render this component*/
return false;
}
componentWillReceiveProps( nextProps ){
/*Here I do manipulations with this._ctx, when new props come*/
}
render(){
return (
<canvas ref={this.saveRef} />
);
}
componentDidMount(){
this._ctx = this._canvas.getContext( "2d" );
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
React community began to deprecate componentWillReceiveProps
in order to replace it with getDerivedStateFromProps
. I can use componentDidUpdate
to perform my drawings, but then I need to remove shouldComponentUpdate
and I will have a lot of useless render calls. What is the correct performant way to update my component in react 16.3, when new props come?
Upvotes: 17
Views: 3887
Reputation: 1912
I found this because I had a sort of similar problem but not completely the same. The solution that works for me is just to put all the relevant code in shouldComponentUpdate
:
(the if
statement used to be in componentWillReceiveProps
)
shouldComponentUpdate (nextProps, nextState) { // no more random renders
if (
(nextProps.nightMode !== this.props.nightMode) ||
(nextProps.language !== this.props.language)
) {
this.props.setRefresh(true) // setTimeout means after current operation
setTimeout(() => this.props.setRefresh(false), 1) // so loading will show for longer than 1ms
}
return this.props.refresh !== nextProps.refresh
}
Upvotes: 0
Reputation: 268235
Use componentDidUpdate
for DOM manipulations like this. A shouldComponentUpdate
won’t really make a difference for a component with a single child that always has the same props. So you should be able to remove it without a significant difference in performance.
If you've profiled the application and determined that in this particular case it does make a difference, you can hoist the element into constructor.
This way React will skip over it completely (which effectively works the same way as shouldComponentUpdate
):
class Canvas extends React.Component {
constructor(props) {
super(props);
this._ctx = null;
this._child = <canvas ref={node => {
this._ctx = node ? node.getContext('2d') : null
} />;
}
componentDidUpdate(prevProps){
// Manipulate this._ctx here
}
render() {
// A constant element tells React to never re-render
return this._child;
}
}
You could also split it into two components:
class Canvas extends React.Component {
saveContext = ctx => {
this._ctx = ctx;
}
componentDidUpdate(prevProps){
// Manipulate this._ctx here
}
render() {
return <PureCanvas contextRef={this.saveContext} />;
}
}
class PureCanvas extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
return (
<canvas
ref={node => node ? this.props.contextRef(node.getContext('2d') : null)}
/>;
}
}
Upvotes: 19