Just to lay the groundwork: upon an action being dispatched, the selector you pass to useSelector()
will be called. If the value it returns is different to the value returned last time an action was dispatched, the component will re-render.
Destructing is indeed the wrong approach, but the top answer here is completely irrelevant. The docs refer to a scenario where the selector is creating a new object every time, like you might do in a mapStateToProps()
function. That would cause the component to re-render every single time an action is dispatched, regardless of what that action does, because the value returned by the selector is technically a different object in memory even if the actual data hasn't changed. In that case, you need to worry about strict equality and shallow equality comparisons. However, your selector is not creating a new object every time. If a dispatched action doesn't modify importApp.productsImport
, it will be the exact same object in memory as before, rendering all of this moot.
Instead, the issue here is that you are selecting an entire slice of state, when you only actually care about a few particular properties of that slice. Consider that importApp.productsImport
probably has other properties besides just open
, importId
, and importProgress
. If those other properties change, then your component will needlessly re-render even though it makes no reference to them. The reason for this is simple: the selector returns importApp.productsImport
, and that object changed. Redux has no way of knowing that open
, importId
, and importProgress
were the only properties you actually cared about, because you didn't select those properties; you selected the whole object.
Solutions
So, to select multiple properties without needless re-renders, you have two options:
- Use multiple
useSelector()
hooks, each selecting a single property in your store.
- Have a single
useSelector()
hook and a single selector that combines multiple properties from your store into a single object. You could do this by:
- Using a memoized selector from reselect.
- Simply writing a function that creates a new object from specific properties of
state
and returns it. If you did this, you would then have to worry about strict equality and shallow equality comparisons.
For this purpose, I feel like multiple useSelector()
hooks is actually the way to go. The docs make a point of mentioning that
Each call to useSelector() creates an individual subscription to the Redux store.
but whether multiple calls would actually incur a real performance penalty compared to a single call purely for this reason, I think, remains to be seen, and it seems to me that worrying about this is probably over-optimisation unless you have a huge app with hundreds or thousands of subscriptions. If you use a single useSelector()
hook, then at that point you're basically just writing a mapStateToProps
function, which I feel like defeats a lot of the allure of using the hook to begin with, and especially so if you're writing TypeScript. And if you then want to destructure the result, that makes it even more cumbersome. I also think using multiple hooks is definitely more in the general spirit of the Hooks API.
createSelector
is necessary. This should be linked to in the redux docs! – Cobber