0
votes

I have a question about redux useSelector equality check.

Refer to the React Redux Hook document (https://react-redux.js.org/api/hooks#equality-comparisons-and-updates)

useSelector will do reference comparison to the return value and the previous value, force re-render if result appears to be different

I have a default blog store state like this

searcherParam: {
  keyword: '',
  categories: [],
  tags: [],
},

In the component, I use useSelector to retrieve the value

const searchParam = useSelector(state => state.Blog.searcherParam)

If I dispatch an action to update searcherParam with same value, the component will re-render because the return value is object (shallow compare)

So I retrieve the value by calling useSelector multiple times

const keyword = useSelector(state => state.Blog.searchrParam.keyword)
const categories = useSelector(state => state.Blog.searchrParam.categories)
const tags = useSelector(state => state.Blog.searchrParam.tags)

And I dispatch an action to update searcherParam with same value again

the component will not re-render

The point I can't understand is why component doesn't re-render ?

If useSelector do reference comparison, the categories value (array) should not be the same reference and the tags as well after dispatching

Do I have any misunderstanding ? Thanks


The reason about not re-rendering is because I save the categories (from redux store) via useState.

and use the useState's value to dispatch, so it is same reference...

here is the codesandbox, sorry for stupid question Q_Q

https://codesandbox.io/s/useselector-test-u6pvg

3

3 Answers

2
votes

So when you do this :

const newKeyword = useSelector(state => state.Blog.searchrParam.keyword)
const newCategories = useSelector(state => state.Blog.searchrParam.categories)
const newTags = useSelector(state => state.Blog.searchrParam.tags)

This simply means that, you would want to have the latest value of each of them.

It does not translate to this :

searcherParam: {
  keyword: newKeyword,
  categories: newCategories,
  tags: newTags,
},

Because searcherParam will always be a new reference when the action is dispatched.

If you just want to re-render, if any of the 3 properties change, then you can simply fetch state.searcherParam and dispatch and achieve a re-render.

Fetching individual properties help when you would want to re-render only on keyword change. You do not want to re-render when either of cateogories or tags have changed.

Note in this case if you just fetch searcherParam, it would not care which of 3 properties have changed, you will get a re-render because it is a new reference.

This is what the docs have mentioned.


Made a trivial implementation. Check below

Edit redux-useSelector-useDispatch-Sample

0
votes

Rather than selecting the above properties separately, they all can be obtained via deconstructing the searchrParam object:

const { newKeyword, newCategories, newTags } = useSelector(
     state => state.Blog.searchrParam
)
0
votes

Since you trying to add same value to the store and also you are referring directly from the store (state.Blog.searchParam), your code below will not cause re-render.

const searchParam = useSelector(state => state.Blog.searcherParam)

In order to make it to re-render, you can try below

const searchParam = useSelector(state => ({keyword: state.keyword, categories: state.categories, tags: state.tags}))

The above code will always try to re-render, since the returned object reference will not be same.