How Exactly useselector works?

Viewed 5216

As per Doc

useSelector(selector: Function, equalityFn?: Function)

useSelector accepts two params and second one is optional since by default it compare strict === reference equality check but not shallow equality.

  const state = useSelector(state => {
        console.log("useSelector rerendering");
        return state.counter
    }, shallowEqual)

Another is

  const state = useSelector(state => {
        console.log("useSelector rerendering");
        return state.counter
    })

In both Cases component rerendering when redux store state changes and also when local state changes then it will render (print console.log inside useSelector)

I really didn't understand exactly how it works

Full source code

import React, { useState } from 'react'
import { shallowEqual, useDispatch, useSelector } from 'react-redux'
import { decrement, increment } from './store/actions'


export default function CounterHooks(props) {

    const [submit, setSubmit] = useState(false)
    const state = useSelector(state => {
        console.log("useSelector rerendering");
        return state.counter
    }, shallowEqual)


    const dispatch = useDispatch()
 
    console.log("component rerendering");
    const increments = () => {

        dispatch(increment());

    }

    const decrements = () => {

        dispatch(decrement());
    }
    const submitButton = () => {
        console.log("component submit", submit);

        setSubmit((previousState) => !previousState)

    }
    return (
        <div>
            <button onClick={increments} >Incrmeent Counter</button>
            <br />
            <button onClick={decrements} >Decrement Counter</button>
            <br />
            <button onClick={submitButton} >Submit</button>
            <br />

            <h2>total : {state.count}</h2> <br />
            <h2>Submit:{String(submit)}</h2> <br />
           
           
        </div>
    )
}

MY question is how exactly second param works ?

2 Answers

In your example, it does not make a difference.

shallowEquals makes sense when you select an object that might be similar in contents, but different by reference.

See these two objects:

const a = { foo: "bar" }
const b = { foo: "bar" }

console.log( a === b ) // will log false
console.log( shallowEquals(a, b)) // will log true

While a and b are two objects with similar shape and contents, they are not the same object. Now shallowEquals does a === comparison between a.foo and b.foo and since both are strings with the same content, a.foo === b.foo will be true.

This does play a role if you create a new object in your selector, say

const result = useSelector((state) => {

  return { a: state.foo.bar, b: state.baz.boo }

})

The result of this will always be a new object, so per default useSelector will always assume they are different, even when state.foo.bar and state.baz.boo actually did not change. If you use a shallowEqual, useSelector will look at the direct (only 1 level deep) child properties of the objects and compare those. Then it will notice that they are in fact equal and not rerender.

Consider the below as reducer,

const userSlice = (
{name: "user", initialState: {name: "test", isLoggedIn: true}, 
reducers: {updateUser(state, action) => (state.isLoggedIn=action.payload["isLoggedIn"])}
}

If we dispatch an action to reducer with same data like below,

const result = useSelector(state => state.users)
dispatch({isLoggedIn: true})

The above code will not cause the component to re-render, since we the state.users we are referring is same as previous one.

We can make it to re-render with the below changes,

    const result = useSelector(state => ({name: state.users.name, isLoggedIn: state.users.isLoggedIn)
dispatch({isLoggedIn: true})

In the above code, we are returning an object directly so whenever the useSelector runs the returned objects will not be same due to referential equality. This make component to re-render

Related