Nam Le
Nam Le

Reputation: 101

useEffect not working when dependency value changed

I have experienced useEffect function from React hook. When I use it with useSelector from React-redux, it worked so weird.

My reducer:

const initialState = { a: 0 };

function mainReducer(state = initialState, action) {
  const { type, payload } = action;
  switch (type) {
    case 'A': return { ...state, a: payload }
    default:
      return state;
  }
}

My Component:

function MyComponent(props) {
  const { a } = useSelector(state => state.mainReducer);
  const dispatch = useDispatch(); 
  
  useEffect(() => {
    console.log('did mount: ', a);
    dispatch({ type: 'A', payload: 1 })
  }, []); 

  useEffect(() => {
    console.log('use effect: ', a);
    dispatch({ type: 'A', payload: a });
  }, [a]) 

  return (<View style={styles.container}>
    <Text style={styles.text}>{a}</Text>
  </View>);
};

Result

Log:

did mount ran: 0
useEffect ran: 0

The last result is variable 'a' = 0

????

As I understand, After the first render, both effect ran sequentially in their order in the code.
(Step 1) So the first effect run fist -> log 'did mount ran: 0'. Then it dispatch value 1 to store
(Step 2) The second effect run after -> log 'did mount ran: 0'. Then it dispatch value 0 to store

But what I don't understand is the second effect must track the change from variable 'a', so there will be:
In the following render time:
(Step 3) the second useEffect should be run when the value 'a' change from 0 to 1 (from Step 1).
And then:
(Step 4) it should have the third re-render when the value change again from 1 to 0 (from Step 2)

So the log should be:

did mount ran: 0
useEffect ran: 0
useEffect ran: 1
useEffect ran: 0

Could you please explain to me what I'm missing? Thank you

Upvotes: 10

Views: 22998

Answers (2)

Amit Chauhan
Amit Chauhan

Reputation: 6919

Your both dispatch are called after first render so even before your second render value is 0 so your second useEffect won't be able detect change as there is no change.

Let's see what is happening in your render method

First Render:

a = 0

first useEffect: dispatch({ a : 1 })

second useEffect: dispatch({ a : 0 })

so now in your redux store a is 0.

Second Render

a = 0

first useEffect: doesn't run as there is no dependency

second useEffect: doesn't run as a hasn't changed.

Upvotes: 5

xadm
xadm

Reputation: 8428

PLEASE, stop using

 useSelector(state => state.mainReducer);

it doesn't make any sense

there should be a simple state transformation (subselection)

const a = useSelector(state => state.a)

taken directly from redux docs:

const counter = useSelector(state => state.counter)  

update

you can see effect (from store change) with slightly changed component

function MyComponent(props) {
  const a = useSelector(state => state.a);
  const dispatch = useDispatch(); 

  console.log('render: ', a);

  useEffect(() => {
    console.log('use effect: ', a);
    dispatch({ type: 'A', payload: a });
  }, [a]) 

  useEffect(() => {
    console.log('did mount: ', a);
    dispatch({ type: 'A', payload: 1 })
  }, []); 

  return (<View style={styles.container}>
    <Text style={styles.text}>{a}</Text>
  </View>);
};

It should result in log:

  • render: 0 // initial state
  • use effect: 0 // first effect run
  • // dispatch 0 ... processed in store by reducer but results in the same state ...
    // ... and in our rendering process we still working on an 'old' a readed from state on the beginning of render
  • did mount: 0 // 'old' a
    // dispatch 1 ... changed state in redux store
  • .... rendered text 0
    ...
    ...

  • // useSelector forces rerendering - change detected

  • render: 1 // latest dispatched value, processed by reducers into new state, rereaded by selector
  • use effect: 1 // useEffect works AS EXPECTED as an effect of a change
  • .... rendered text 1

...
...

  • no more rerenderings - latest dispach not changed state

Of course dispatch from other component will force update in this component ... if value will be different.

Upvotes: 1

Related Questions