Be wary of that shallowEqual

05 Jan 2017

Given that shallowEqual function is being used in
conjunction with React’s shouldComponentUpdate hook a lot these days,
this tip might be of help. shallowEqual is a function that checks whether
two objects are equal based on the first level of key <-> value pairs.
That is, the following would result in true:

If the value of any key is an object or a function (UPDATE: I’ve added
some more information on this case here), the response
will be false. A common mistake that you might be doing is use a
shouldComponentUpdate function with shallowEqual comparison
function, but pass a callback as a prop. For example:

Since one of the props in the above code is a function or a complex
object that’s getting passed from the parent, the child’s
(MyComponent) shouldComponentUpdate will always return true, which
in turn triggers a re-render of the component, and all its children.
This is particularly problematic when this component has a deeply nested
structure. Do watchout for this pattern. Note that this is not exactly a
problem with the implementation of the function, but the way it’s used.

In the current codebase I’m working on, there are two utilility
functions omit and pick that work on objects similar to how the
identically named functionsin Lodash
work. I’ve started using these to filter out props to
include just the ones I need. For example:

Good thing about Lodash is that both these functions are available as
individual modules, so use them at will. It also has a
_.isEqual function that performs a deep-check of
objects. This will solve the deep object problem somewhat, but won’t
solve it when one of the values in the object is a function.

Update

The case of function is a bit complicated than the one provided in the
example above. The naive way of passing a function is doing something
like:

This type of code creates a binding for each render, so the func
prop gets a new function every time the component renders. This is
when the simpler form of shallowEqual fails. A different way to
achieve the same effect, but without creating the new binding every time
is by defining a property function for the class: