Мутировать prevState в React (установить состояние с функцией)

#javascript #reactjs #react-redux

Вопрос:

Пожалуйста, подробно объясните, почему этот код приводит к ошибке. Я прочитал статью с решением этой проблемы (счетчики const = […prevState.счетчики];). Но почему это происходит, мне непонятно.

Когда вы нажимаете на кнопку, вы видите, что счетчик увеличивается в два раза. Мы ожидаем увеличения на 1. Почему мутация prevState вызывает увеличение счетчика на 2???

 // App.js
import React, { Component } from 'react';
import Counter from './Counter';

export default class App extends Component {
  state = {
    counters: [0, 0],
  };

  clickHandler = index => {
    this.setState((prevState, prevProps) => {
      let counter = prevState.counters[index];

      const counters = prevState.counters;
      counters[index] =   counter;
      return {
        counters,
      };
    });
  };
  render() {
    const counters = this.state.counters.map((counter, index) => (
      <Counter
        counter={counter}
        key={index}
        clickHandler={() => this.clickHandler(index)}
      />
    ));
    return <React.Fragment>{counters}</React.Fragment>;
  }
}
 
 // Counter.js
import React from 'react';

const Counter = ({ counter, clickHandler }) => {
  return (
    <React.Fragment>
      <div>{counter}</div>
      <button onClick={clickHandler}>Increment the counter above!</button>
    </React.Fragment>
  );
};

export default Counter;
 

Комментарии:

1. Ты уже завернул свое App в <React.StrictMode> это ?

2. Этот код из статьи [ irensaysblog.netlify.app/веб-разработка/react-setstate]

3. @GabrielePetrioli — это из-за React.StrictMode в сочетании с мутирующим состоянием. Забавное название статьи — React setState: the **Right** Way 🤦

4. @Ilya — он говорит If you work with objects from your state, always copy them to avoid unintended modifications , а затем явно пишет код, который делает обратное. Нет, это не «хорошая» статья.

5. Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions: ... Документы. Видите, как это uage помогло указать, что код делает что-то не так? Посмотрите, как именно для этого был разработан StrictMode

Ответ №1:

Правило № 1 реакции: НЕ ИЗМЕНЯЙТЕ СОСТОЯНИЕ.

     this.setState((prevState, prevProps) => {
      let counter = prevState.counters[index];

      const counters = prevState.counters;
      counters[index] =   counter; <-- THIS LINE MUTATES counters
      return {
        counters,
      };
    });
 

Должно быть:

     this.setState((prevState) => ({
        // return a new array with map, DON't mutate the old one
        counters: prevState.counters.map((c,i) => i === index ? c   : c);
    }));
 

Вы видите двойное увеличение, потому что:

  1. Вы используете React.StrictMode и
  2. Вы находитесь в мутирующем состоянии.

Так что суть все еще в том, ЧТОБЫ НЕ ИЗМЕНЯТЬ СОСТОЯНИЕ.

Если вы используете мой пример с React.StrictMode, вы увидите, приведет ли это к одному приращению. Это потому, что статья под названием «правильный путь» на самом деле показывает вам НЕПРАВИЛЬНЫЙ путь.

Если вы используете React.StrictMode и замечаете логические различия, это потому, что вы делаете что-то неправильно. Я уже упоминал выше, что вы делаете неправильно, вы мутируете состояние (как показано в статье).

Никогда, никогда, никогда, никогда не изменяйте состояние. Всегда возвращайте новые объекты/массивы, никогда не обновляйте существующие.

Комментарии:

1. Большое спасибо за ответ. Я понял. Я знаю это решение. Но цель моего вопроса заключается в том, почему текущий счетчик увеличился в два раза 😊 потому что prevState=this.state или что-то еще?

2. @Илья — забавно, как в статье говорится «правильный путь», и это изменяет состояние. Несмотря на то, что это все еще «работает», мутирующее состояние приводит к труднодоступности ошибок. Никогда не делай этого. Тем не менее, ваш код отлично работает, когда я помещаю его сюда: stackblitz.com/edit/react-d1stpk?file=src/App.js

3. @Илья Ах, это из React.StrictMode -за того, как комментатор сказал: stackblitz.com/edit/react-ojpcr7?file=src/index.js

4. @Илья — в сочетании с тем, что у тебя мутирующее состояние. React.StrictMode не должен приводить к логическим изменениям. Если вы видите логические изменения, это потому, что вы делаете что-то неправильно, что вы и есть, что является мутацией состояния (спасибо даунвотеру).

5. ты самый лучший! Это очень полезная информация