Почему этот обработчик событий onClick запускается дважды в моем create-react-app

#javascript #reactjs #create-react-app

#javascript #reactjs #create-react-app

Вопрос:

может кто-нибудь сказать мне, почему этот обработчик onClick с повышенным рейтингом запускается дважды? журналы будут указывать, что он запускается только один раз, но оценка, которую он контролирует, увеличивается на 2

 export default class Container extends Component {
  constructor(props) {
    super(props);

    this.state = {
      jokes: [],
    };
    this.getNewJokes = this.getNewJokes.bind(this);
    this.retrieveJokes = this.retrieveJokes.bind(this);
    this.upVote = this.upVote.bind(this);
  }
upVote(id) {
    this.setState(state => {
      //find the joke with the matching id and increase score by one
      const modifiedJokes = state.jokes.map(joke => {
        if (joke.id === id) {
          
          joke.score = joke.score   1;
          
        }
        return joke;
      });
      console.log(modifiedJokes);
      return { jokes: modifiedJokes };
    });
  }
render() {
    return (
      <div>
        <h1>Container</h1>
        {this.state.jokes.map(joke => (
          <Joke
            key={joke.id}
            id={joke.id}
            joke={joke.joke}
            score={joke.score}
            upVote={this.upVote}
            downVote={this.downVote}
          />
        ))}
      </div>
    );
  }
}
 

с другой стороны, если я перепишу обработчик таким образом, он срабатывает только один раз

 upVote(id) {
    const modifiedJokes = this.state.jokes.map(joke => {
      if (joke.id === id) {
        joke.score = joke.score   1;
      }
      return joke;
    });
    this.setState({ jokes: modifiedJokes });
  };
 

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

1. где вы вызываете обратный upVote() вызов? что такое Joke component?

2. Может быть, ему слишком понравилась шутка?

3. вы пробовали устанавливать точки останова? Это поможет вам увидеть, какой код выполняется и когда.

Ответ №1:

Мое лучшее предположение заключается в том, что в первом случае вы также изменяете состояние напрямую, когда выполняете joke.score = joke.score 1;

Потому что вы выполняете это сопоставление непосредственно с переменной массива состояния, а в Javascript при использовании массива вы работаете только с указателем на этот массив, а не создаете копию этого массива.

Таким образом, функция сопоставления, вероятно, берет неглубокую копию массива, и там возникает проблема.

Вы можете использовать Lodash для создания глубокой копии массива состояний, прежде чем приступить к работе с ним, что не вызовет у вас проблем:

https://codesandbox.io/s/great-babbage-lorlm

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

1. это правильный ответ. Я отвечу на свой собственный вопрос и уточню

Ответ №2:

Это не работает, потому что React использует синтетические события, которые повторно используются при выполнении обработки. Вызов функции form of setState отложит оценку (это может быть опасно, когда setState вызов зависит от события и событие потеряло свое значение).

Так что это тоже должно сработать:

 this.setState((state,props) => ({ jokes: modifiedJokes}));
 

На данный момент я не могу найти свой источник, но я помню, что сталкивался с этим некоторое время назад. Я уверен, что просмотр документов React может дать более подробное объяснение

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

1. Нашел соответствующую документацию по реакции setState с примером

2. я понял, что это часть setState, которая запускается дважды. Остальная часть обработчика onclick запускается только один раз … каким-то образом? в любом случае, пока я применяю изменения к новому состоянию за пределами обратного вызова setstate, он работает нормально. Но это противоречит всему смыслу возможности изменять состояние на основе старого состояния, поэтому я использую setstate обратного вызова вместо обычного

3. Верно, в основном то, что я тоже говорил. Обработчик событий может использоваться повторно после того, как состояние было обновлено один раз. Таким образом, повторное использование создает впечатление, что оно было вызвано во второй раз. Если это имеет смысл? Хорошая работа с отладкой и хорошо оставить ответ для других 🙂

Ответ №3:

Я выяснил, что было не так. Я опубликую ответ на всякий случай, если кто-нибудь случайно наткнется на это, столкнувшись с той же проблемой.

Когда в режиме отладки react дважды запускает определенные функции, чтобы препятствовать определенным операциям, которые могут привести к ошибкам. Одной из этих операций является прямое изменение состояния. при выполнении

 this.setState(state => {
      //find the joke with the matching id and increase score by one
      const modifiedJokes = state.jokes.map(joke => {
        if (joke.id === id) {
          
          joke.score = joke.score   1;
          
        }
        return joke;
      });
      console.log(modifiedJokes);
      return { jokes: modifiedJokes };
 

функция map возвращает новый массив, в котором все элементы указывают на один и тот же элемент в исходном массиве. поэтому, выполнив

 
state.jokes.map(joke => {
        if (joke.id === id) {          
          joke.score = joke.score   1;          
        }
 

Я фактически напрямую изменял состояние. React дважды запускает функцию setstate в режиме отладки, чтобы исключить такого рода операции.

таким образом, правильный «способ реагирования» на изменение атрибута объекта, вложенного в массив, заключается в том, чтобы сделать это вместо

 this.setState(state =>{
   let modifiedJokes = state.jokes.map(joke => {
        
        if (joke.id === id) {          
          return {...joke, score:joke.score 1}         
        }
        else{ return joke}
   })
   return {jokes:modifiedJokes}
}) 
 

таким образом, при обнаружении элемента с правильным идентификатором создается копия этого конкретного элемента, и оценка этой копии увеличивается на единицу, что не влияет на фактический элемент, который все еще находится в состоянии, оставленном нетронутым, пока он не будет изменен реакцией, передающей новое состояние