Hi,
i am building component which acts only as wrapper for some other generated content and uses third party library. This library works with props.children
of component. So far so good, but this thrird party library is little laggy when applied, or refreshed on element. And because only reason to refresh this library is when props.children
changed I am trying to figure how to compare this.props.children
and nextProps.children
in shouldComponentUpdate
. I was thinking that PureRenderMixin should do the work, but for me it does not works. Component is rerendered even if I change only state.listName
as it is in example below.
<div>
List name '{this.state.listName}'
<br />
<MyComponent>
<ul>
{listOfLi}
</ul>
</MyComponent>
</div>
Is there any way, how to manage comparing of props.children
or any other option how to do something like that?
Thanks for any help!
In order for props to change, they need to be updated by the parent component. This means the parent would have to re-render, which will trigger re-render of the child component regardless of its props.
You can use React. Children to iterate over the children, and then clone each element with new props (shallow merged) using React. cloneElement .
React allows us to render one component inside another component. It means, we can create the parent-child relationship between the 2 or more components. Prerequisites: The pre-requisites for this project are: React Knowledge.
componentDidMount runs only after a re-render. A component is re-renders only by state changes not prop changes. If you want to change state on prop change use componentDidUpdate or getDerivedStateFromProps .
You can make use of child key
prop that React suggests that arrays of children should be given to uniquely identify them. Because each child has a key, you can reliably tell whether children has changed across prop changes (this is the entire point of keys!). If the keys don't match between new and old then they have changed.
React.render(<App><Child key='1'/><Child key='2'/></App>, document.body)
and do the check in the App component you want to check before each update if the children changed
shouldComponentUpdate(nextProps){
var oldKeys = this.props.children.map( child => child.key);
var newKeys = nextProps.children.map( child => child.key);
//compare new and old keys to make sure they are the same
}
Note that this doesn't tell you if the content of each child has changed, you have to compare each by some criteria (such as deeply comparing props) if you want to know if nothing in the whole tree below this point has changed
as an even further optimization we know that children will never change as result of a state change so we can actually do our comparing in componentWillReceiveProps()
and just set some state property like childrenHaveChanged
As Matt S pointed out, the accepted answer is kind of a fragile workaround and would depend on a non-standard use of key
. Aside from the list examples he listed, even using something like key={id}
would fall down if your id
s remained the same but certain fields were modified in the resources they represent.
This issue contains a good discussion on the topic and ends with a more stable workaround. Essentially, you can simplify the children
prop in a way that allows you to run a deep comparison. You can use the React.Children
utilities to write the simplification methods:
// Flattens all child elements into a single list
const flatten = (children, flat = []) => {
flat = [ ...flat, ...React.Children.toArray(children) ]
if (children.props && children.props.children) {
return flatten(children.props.children, flat)
}
return flat
}
// Strips all circular references and internal fields
const simplify = children => {
const flat = flatten(children)
return flat.map(
({
key,
ref,
type,
props: {
children,
...props
}
}) => ({
key, ref, type, props
})
)
}
Then you can use shouldComponentUpdate
or React.memo
to prevent re-renders:
const MyComponent = ({ children }) => (
<div>{ children }</div>
)
export default React.memo(MyComponent, (prev, next) => (
JSON.stringify(simplify(prev.children)) ===
JSON.stringify(simplify(next.children))
))
These utilities + JSON.stringify
are just one approach, the one mentioned in the comment is similar and you could also leverage utilities like lodash.isequal
for the deep comparison. Unfortunately I don't know of any one or two liners for this comparison but please comment if you know a simpler stable way to do this!
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