setState не является постоянным

#javascript #reactjs #render #setstate

#javascript #reactjs #визуализация #setstate

Вопрос:

Я пытаюсь создать sidemenu, который имеет сворачиваемые параметры.

Ниже приведен мой код:

 export default class CRSideMenu extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fprActive: true
    };
    autoBind(this);
  }

  toggleFPR() {
    console.log('Setting fprActive from ', this.state.fprActive, ' to ', !this.state.fprActive);
    this.setState({
      fprActive: !this.state.fprActive
    });
  }

  render() {
    console.log('render', this.state.fprActive);
    return (
      <ul className='list-group list-group-nav'>
        <li>
          <a 
            href='#' 
            className={classnames('list-group-item', this.state.fprActive amp;amp; 'active', 'has-subnav')}
            onClick={this.toggleFPR} >
            FPR
          </a>
          <ul className='list-group list-group-nav'>
            <li>
              <a href='' className='list-group-item'>FR</a>
            </li>
          </ul>
        </li>
      </ul>
    );
  }
}
  

Когда я распечатал this.state.fprActive в методе render (), я вижу следующее:

  • Изменение значения fprActive с true на false
  • отображать false
  • отображать true

Почему мой fprActive автоматически возвращается к «true», когда я нажимаю только один раз?

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

1. Я предполагаю, что autoBind делает toggleFDR привязку к this ?

Ответ №1:

На этом этапе я не могу воспроизвести проблему, но симптом говорит о том, что ваша страница обновляется при нажатии на привязку, потому что вы не предотвращаете действие по умолчанию. Есть toggleFPR вызов preventDefault для объекта события, который он получает:

 toggleFPR(event) {
//        ^^^^^ ------------ ***
  event.preventDefault(); // ***
  console.log('Setting fprActive from ', this.state.fprActive, ' to ', !this.state.fprActive);
  this.setState({
    fprActive: !this.state.fprActive
  });
}
  

Отдельно: здесь вы нарушаете одно из фундаментальных правил React:

 console.log('Setting fprActive from ', this.state.fprActive, ' to ', !this.state.fprActive);
this.setState({
  fprActive: !this.state.fprActive
});
  

При настройке состояния на основе существующего состояния вы должны использовать версию обратного вызова, а не версию, в которую вы передаете объект:

 this.setState(({fprActive}) => {
  console.log('Setting fprActive from ', fprActive, ' to ', !fprActive);
  return {fprActive: !fprActive};
});
  

Если вы этого не сделаете, он будет работать большую часть времени и иногда будет давать сбой по труднодиагностируемым причинам.

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

1. Это работает отлично! Кое-что, что я заметил, это то, что когда я поменял местами теги <a></a> для тега <div></div> , повторный рендеринг страницы отсутствует (без event.preventDefault()). Кажется, что под капотом вызываются теги <a>

2. @AKJ — Я добавил примечание в конец ответа, ваш setState вызов нарушает одно из фундаментальных правил React, поэтому вы захотите это исправить. Приятного кодирования!

3. большое вам спасибо за улучшение кода. Могу ли я узнать, откуда ({fprActive}) взялось? А также я внес эти изменения, но я хочу понять это, а не копировать вслепую 🙂

4. @AKJ — Это разрушает список параметров. Версия обратного вызова setState получает объект текущего состояния в качестве своего аргумента. При использовании {fprActive} в списке параметров из него извлекается только fprActive свойство. Кстати, я рад, что это сработало, потому что я не смог воспроизвести проблему изолированно … 🙂

5. другими словами, если у меня есть другие атрибуты (скажем, attr1 amp; attr2) в ` this.state = {…}` в конструкторе, я мог бы сделать это также: this.setState(({fprActive, attr1, attr2}) => {...}); ?