Destructive sorting of React useState hook variable?

R. Michael Rogers :

One of the props into this component is an array of values, and I'd like to sort those values. I've done so with the following:

const MyComponent = items => {
    const [sortedItems, setSortedItems] = useState(items)

    useEffect(() => sortedItems.sort(), [sortedItems])

    /* rest of component code */
}

It definitely seems like it doesn't fit with the spirit of React and Hooks, but it works. I'm not looking for alternate implementation approaches, but what I'm super curious about is: is the approach here counter-indicated by the React documentation?

stackoverflow-newbie :

is the approach here counter-indicated by the React documentation?

Yes. Normally, updating the state should call its setState function, but we see here that using sort mutates sortedItems without calling setSortedItems. The approach to use setSortedItems instead can be implemented like so:

useEffect(() => setSortedItems([...sortedItems].sort()), [sortedItems])

(I know you said not looking for alternative approaches, but please take this as an example so I can explain.)

The important point is that this will not mutate the previous sortedItems before triggering a render. This is so that the next DOM render and anywhere that reads sortedItems will be consistent.


Update

Although the render and accesses to sortedItems will be consistent, it may not contain the value that's expected in subsequent updates because of how React's state updates are queued asynchronously (this issue is also prevalent in OP's example). The issue arises when the time to sort subsequent sortedItems vary greatly, but there can be other causes as well.

React docs explains this behavior in their docs for setState:

https://reactjs.org/docs/react-component.html#setstate

Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the current state, we recommend using the updater function form, instead:

this.setState((state) => {
   return {quantity: state.quantity + 1};
});

In our example, it's a minor change of adding a callback to the example:

useEffect(() => setSortedItems((prevSortedItems) => [...prevSortedItems].sort()), [sortedItems])

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=10479&siteId=1