3

I am working on my project and i noticed that when strict mode is turned on it pushes two identical elements into my array. When the strict mode is off it pushes only one element into the array. Is there any explanation why this happens?

import {getRandomNumber} from './utils';


export function registerTicket() {
    const newTicket = {
        number: getRandomNumber(),
        color: 'red',
    }
    this.setState((prevState) => {
        prevState.tickets.push(newTicket);
        return {
            remainingTickets: --prevState.remainingTickets,
            tickets: prevState.tickets,

        }
    })
}

Here is my state.

    this.state = {
      winningNumber: getRandomNumber(),
      remainingTickets: 5,
      tickets: [],
      finished: false
    };
3
  • Check may help you out stackoverflow.com/questions/53183362/… Commented Jul 21, 2021 at 7:25
  • 1
    Are you looking for other answers on SO or something from the official docs? Long story short it helps expose unintentional side-effects by double-invoking certain lifecycle methods and React hooks. In your case you are mutating the state object. Commented Jul 21, 2021 at 7:28
  • Where and how are you calling registerTicket? And what is this, are you working with a class component? Commented Jul 15, 2023 at 14:13

2 Answers 2

6

React StrictMode helps expose unintentional side-effects by intentionally double-invoking certain lifecycle methods and React hooks. The setState updater function is one that is called twice. I'll leave the in-depth explanation to the linked questions.

Detecting unexpected side-effects

Your unintentional side-effect is a state mutation of the previous tickets state when you push a new element into it and when you pre-decrement the remainingTickets state. The hat-trick is that you also don't return a new tickets array referece.

this.setState((prevState) => {
  prevState.tickets.push(newTicket); // <-- mutation!!
  return {
    remainingTickets: --prevState.remainingTickets, // <-- mutation!!
    tickets: prevState.tickets, // <-- same old array reference
  }
})

The solution is to shallow copy the previous state array into a new array reference and append the new element.

this.setState((prevState) => ({
  remainingTickets: prevState.remainingTickets - 1,
  tickets: [...prevState.tickets, newTicket],
}));
Sign up to request clarification or add additional context in comments.

11 Comments

oh so the setstate is called twice ?
@ProHunter67 The callback function passed to setState is invoked twice, but essentially, yes, that is the effect.
@ProHunter67 Because you aren't mutating anything. In both invokations you are applying the same update to the same previous state, the result is identical each time.
@ProHunter67 Yes. Think of it more like React just calling the updater function that computes the next state (that's what it is really) twice but only storing the result for the next render once. If there are zero side-effects in the function, then you see the same result as if it had been called only once. BUT if there are side-effects and/or mutations, these can leak out and be exposed. The first call did the array.push and mutated previous state, and then a second call did another array.push and mutated it again, and then on the next render you see the result of both pushes.
@ProHunter67 Yeah, confirmed the behavior here. TBH I think the answer you seek lies in the ReactDOM and reconciliation code for how it handles state updater functions and StrictMode. Most of my knowledge is from the docs and empirical experience.
|
0

React.StrictMode causes the render function to be called twice in development, to identify unintended side-effects

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.