简体   繁体   中英

Reset state on prop change

Lets say I have these two components:

const availableTasks = [1, 2, 3];

const Task = () => {
    const [current, setCurrent] = React.useState(0);
    const getNextTask = () => current + 1 < availableTasks.length ? current + 1 : 0;

    return (
        <div className='task-container'>
            <div className='task-information'>
                Some information.
            </div>
            <TaskVote id={availableTasks[current]} key={current}/>
            <button onClick={() => setCurrent(getNextTask())}> Next Task</button>
        </div>
    );
};
const TaskVote = ({ id }) => {
    const [count, setCount] = React.useState(0);

    React.useEffect(() => {
        setInterval(() => setCount(count => count + 1), 1000); // Async data receiving (e.g. websocket)
    }, []);

    return <div> Counting for: {id}, Counted: {count}</div>;
};

TaskVote receives its data from websockets, and updates count state when needed. (Replaced it with interval, for the example)

Task renders some information about the task, it renders TaskVote , and a "Next Task" button .

When user is skipping to the next task, TaskVote receives new key, so it will re-mount, which is fine.

In case there is only one element in availableTaks , the key prop won't change, so count state will not reset, even though I want it to reset once the button is clicked (even if it's the same Task).

How can I handle this case?

Thanks!

Your problem is that you use a simple number as the key which does not update if your next task is the same as before.

This leads to the following rendering multiple times: <TaskVote id={1} key={0}/>

React cannot determine that something changed.

A simple solution is to keep a separate state which can be used as a key.

const [voteState, setVoteState] = useState(0)
...
<TaskVote id={availableTasks[current]} key={voteState}/>
<button onClick={() => {
  setCurrent(getNextTask())
  setVoteState((s) => s+1) // always increasing
}}> Next Task</button>

However, setting multiple state values can lead to multiple rerenders. You can optimize this by merging multiple values (or by useReducer):


const [current, setCurrent] = React.useState({ task: 0, voteButtonState: 0 });

... 
<TaskVote id={availableTasks[current]} key={current.voteButtonState}/>
<button onClick={() => {
  setCurrent((old) => {
    return {
      task: getNextTask(),
      voteButtonState: old.voteButtonState + 1 // always increasing
    }
  }) 
}> Next Task</button>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM