As mentioned by Dan Abramov
Do it right inside render
We actually use that approach with memoise one for any kind of proxying props to state calculations.
Our code looks this way
// ./decorators/memoized.js
import memoizeOne from 'memoize-one';
export function memoized(target, key, descriptor) {
descriptor.value = memoizeOne(descriptor.value);
return descriptor;
}
// ./components/exampleComponent.js
import React from 'react';
import { memoized } from 'src/decorators';
class ExampleComponent extends React.Component {
buildValuesFromProps() {
const {
watchedProp1,
watchedProp2,
watchedProp3,
watchedProp4,
watchedProp5,
} = this.props
return {
value1: buildValue1(watchedProp1, watchedProp2),
value2: buildValue2(watchedProp1, watchedProp3, watchedProp5),
value3: buildValue3(watchedProp3, watchedProp4, watchedProp5),
}
}
@memoized
buildValue1(watchedProp1, watchedProp2) {
return ...;
}
@memoized
buildValue2(watchedProp1, watchedProp3, watchedProp5) {
return ...;
}
@memoized
buildValue3(watchedProp3, watchedProp4, watchedProp5) {
return ...;
}
render() {
const {
value1,
value2,
value3
} = this.buildValuesFromProps();
return (
<div>
<Component1 value={value1}>
<Component2 value={value2}>
<Component3 value={value3}>
</div>
);
}
}
The benefits of it are that you don't need to code tons of comparison boilerplate inside getDerivedStateFromProps
or componentWillReceiveProps
and you can skip copy-paste initialization inside a constructor.
NOTE:
This approach is used only for proxying the props to state, in case you have some inner state logic it still needs to be handled in component lifecycles.