1

I am trying to compare old and new state values using custom hook usePrevious made with useRef hook where state consists of array of objects.

While printing the old value and current value, it returns current values in both cases, but when it's just the array of numbers or if it's the first render, it works well.

Also, https://codesandbox.io/s/4c4ie is the code for the test.

Is there any mistake I have done or there is something else to do to get old state and current state?

Below is the code I am using.

import React from 'react'

function usePrevious(value) {
    const ref = React.useRef();
    React.useEffect(() => {
        ref.current = value;
    });
    return ref.current;
}

function Playground() {

    const [state, setState] = React.useState([{ value: 0 }]);


    const prevState = usePrevious(state);
    React.useEffect(() => {
        console.log(prevState, state)
        if (prevState !== state) {
            try {
                console.log(prevState[0].value)
                console.log(state[0].value)
            } catch (e) {

            }
        }
    }, [JSON.stringify(state)])
    // }, [state])


    const _onClick = () => {
        const tempState = [...state];
        tempState[0].value = state[0].value + 1;
        setState(tempState)
    }


    return (
        <div>
            <div>prevStateValue: {prevState ? prevState[0].value : 'undefined'}</div>
            <div>stateValue: {state[0].value}</div>
            <button onClick={_onClick}>click</button>
        </div>
    )
}

export default Playground
2
  • 1
    It seems like you forgot deps array in your usePrevious` useEffect. It should be [value]. See usehooks.com/usePrevious for reference Commented Jun 30, 2020 at 12:23
  • @user0101 no it didn't work. Commented Jun 30, 2020 at 12:41

2 Answers 2

1

You were mutating state with: tempState[0].value = state[0].value + 1;

Here is a working snippet:

function usePrevious(value) {
  const ref = React.useRef();
  React.useEffect(() => {
    ref.current = value;
  },[value]);//only set when value changed
  return ref.current;
}

function App() {
  const [state, setState] = React.useState([{ value: 0 }]);

  const prevState = usePrevious(state);
  React.useEffect(() => {
    if (prevState !== state) {
      try {
        console.log(
          'pref',
          prevState[0].value,
          'current',
          state[0].value
        );
      } catch (e) {
        console.log('not set yet');
      }
    }
  }, [prevState, state]);
  // }, [state])

  const _onClick = () => {
    const tempState = [...state];
    //you were mutating state here
    tempState[0] = {
      ...tempState[0],
      value: tempState[0].value + 1,
    };
    setState(tempState);
  };

  return (
    <div>
      <div>
        prevStateValue:{' '}
        {prevState ? prevState[0].value : 'undefined'}
      </div>
      <div>stateValue: {state[0].value}</div>
      <button onClick={_onClick}>click</button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

Sign up to request clarification or add additional context in comments.

Comments

1

You are passing the reference of state to ref instead of the value. Cloning the object before assigning to ref.current will help.

function usePrevious(value) {
  const ref = React.useRef();
  React.useEffect(() => {
    ref.current = JSON.parse(JSON.stringify(value));
  });
  return ref.current;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.