0

I have two components.

function Parent(props){

  const handleClick = () => {
   console.log(props.stateA);
  };

  return <div><Child text={stateB} handleClick={handleClick} /></div>
}

const mapStateToProps = (state) => {
  return {
    stateA: state.stateA // stateA will be changed somewhere else
    stateB: state.stateB
  }
};

export default connect(mapStateToProps)(Parent);
function Child(props) {
   return <div onClick={props.handleClick}>{props.text}</div>
}

export default React.memo(Child,(prev, next) => {
   return prev.text === next.text
});

My problem is when stateA is changed somewhere, clicking on Child will log the previous stateA. I can't access the latest stateA.

You can see, I don't want to Child re-render when stateA changes,it should re-render only when stateB changed. But I want to access the latest stateA in Parent when clicking on Child.

Is there any method to solve this problem?

Tom
  • 770
  • 9
  • 18

4 Answers4

0

If the Parent component is a functional component then you can use like this

 const [valueA, setValueA] = useState('')
 useEffect(() => {
      setValueA(props.stateA)
 },[props.stateA])

 console.log(valueA) // latest Value of stateA
 return <div><Child text={stateB} handleClick={handleClick} /></div>

I hope it'll work for you.

Jay Parmar
  • 368
  • 2
  • 9
  • Sorry, it doesn't work. Your method is like this problem https://stackoverflow.com/questions/57543996/react-hooks-issue-with-react-memo-and-usestate – Tom Sep 29 '20 at 16:26
0

You should be able to access props.stateA no problem

const handleClick = () => {
   console.log(props.stateA);
};

because you accessing parent's props in handleClick. So if props.stateA is stale then the logical conclusion is the parent doesn't receive the latest props. Can we see how you update props/state?

Aleks
  • 894
  • 10
  • 14
  • The Parent received stateA indeed. I have tried {props.stateA} to show it in dom – Tom Sep 29 '20 at 15:58
  • So props.stateA is not stale? try to test this `const handleClick = useCallback(() => { console.log(props.stateA); },[props.stateA]);` – Aleks Sep 29 '20 at 16:07
  • It still get the same result. I know it's just like this problem https://stackoverflow.com/questions/57543996/react-hooks-issue-with-react-memo-and-usestate But I don't know how to solve it in my situation. – Tom Sep 29 '20 at 16:24
  • I see. There is no easy solution in that case because Child will always have a stale version of `handleClick` with your `memo` condition. I would suggest to drop memo from parent and only use it on a Child that actually needs it. I think I've learned something new myself – Aleks Sep 29 '20 at 17:26
0

You can keep a ref to stateA so it is what is logged when you call handleClick. useRef ensures that the last value is used.

function Parent(props){
  const stateARef = useRef(props.stateA);

  useEffect(() => {
    stateARef.current = props.stateA;
  }, [props.stateA])

  const handleClick = () => {
   console.log(stateARef.current);
  };

  return <div><Child text={stateB} handleClick={handleClick} /></div>
}
lmonninger
  • 831
  • 3
  • 13
Feder 240516
  • 61
  • 1
  • 2
  • "Ensures that the last value is used is imprecise." Referring to the [documentation](https://reactjs.org/docs/hooks-reference.html#useref), "the returned object [ref] will persist for the full lifetime of the component." – lmonninger May 13 '22 at 06:50
0

The problem you are experiencing has nothing to do with Redux.

The Parent component passes 2 props to the child: the text which is changed when needed and handleClick which is changed each render of the Parent component - a new function is created each time.

But the React.memo is checking only the text prop, so the child receives a stale handleClick quite often.

The correct solution is to wrap the handleClick with useCallback and check all props in React.memo (react does this by default).

function Parent(props){

  const handleClick = useCallback(() => {
   console.log(props.stateA);
  }, []);

  return <div><Child text={stateB} handleClick={handleClick} /></div>
}

const mapStateToProps = (state) => {
  return {
    stateA: state.stateA // stateA will be changed somewhere else
    stateB: state.stateB
  }
};

export default connect(mapStateToProps)(Parent);

function Child(props) {
   return <div onClick={props.handleClick}>{props.text}</div>
}

export default React.memo(Child);
Alissa
  • 1,824
  • 1
  • 12
  • 15