Подтвердить что ты не робот

Почему я не могу напрямую изменить состояние компонента?

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

Я хотел бы понять, почему я не могу просто напрямую изменить состояние, а затем (в той же функции) вызвать this.setState({}) просто для запуска render.

Например, приведенный ниже код работает нормально:

const React = require('react');

const App = React.createClass({
    getInitialState: function() {
        return {
            some: {
                rather: {
                    deeply: {
                        embedded: {
                            stuff: 1
                        }}}}};
    },
    updateCounter: function () {
        this.state.some.rather.deeply.embedded.stuff++;
        this.setState({}); // just to trigger the render ...
    },
    render: function() {
        return (
                <div>
                Counter value: {this.state.some.rather.deeply.embedded.stuff}
                <br></br>
                <button onClick={this.updateCounter}>inc</button>
                </div>
        );
    }
});

export default App;

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

Примечания под документацией this.setState основном идентифицируют две ошибки:

  1. Что если вы изменяете состояние напрямую, а затем вызываете this.setState это может заменить (перезаписать?) Выполненную вами мутацию. Я не вижу, как это может произойти в приведенном выше коде.
  2. Этот setState может эффективно мутировать this.state в асинхронном/отложенном режиме, поэтому при доступе к this.state сразу после вызова this.setState вам не гарантирован доступ к окончательному мутированному состоянию. Я понимаю, что это не проблема, если this.setState является последним вызовом функции обновления.
4b9b3361

Ответ 1

Документы React для setState имеют следующее:

НИКОГДА не this.state напрямую, поскольку setState() вызов setState() может заменить сделанную вами мутацию. Относитесь к this.state состоянию как к неизменному.

setState() не this.state немедленно this.state но создает ожидающий переход в состояние. Доступ к this.state после вызова этого метода может потенциально вернуть существующее значение.

Нет гарантии синхронной работы вызовов setState и вызовы могут быть пакетированы для повышения производительности.

setState() всегда будет запускать повторную визуализацию, если логика условной визуализации не реализована в shouldComponentUpdate(). Если используются изменяемые объекты и логика не может быть реализована в shouldComponentUpdate(), вызов setState() только в том случае, если новое состояние отличается от предыдущего, позволит избежать ненужных повторных визуализаций.

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

Относительно ваших расширенных вопросов 1) и 2) setState() не является немедленным. Он ставит в очередь переход состояния в зависимости от того, что, по его мнению, происходит, что может не включать прямые изменения в this.state. Поскольку он ставится в очередь, а не применяется немедленно, вполне возможно, что что-то будет изменено между ними так, что ваши прямые изменения будут перезаписаны.

Если ничего другого, вам может быть лучше, если учесть, что прямое изменение this.state может считаться хорошей практикой. Вы можете лично знать, что ваш код взаимодействует с React таким образом, что эти перезаписи или другие проблемы не могут возникнуть, но вы создаете ситуацию, в которой другие разработчики или будущие обновления могут внезапно оказаться со странными или тонкими проблемами.

Ответ 2

Этот ответ должен предоставить достаточную информацию, чтобы не изменять/мутировать состояние непосредственно в Реактите.

React следует Однонаправленный поток данных. Смысл, поток данных внутри реакции должен и, как ожидается, будет проходить по круговой траектории.

Реакция потока данных без потока

введите описание изображения здесь

Чтобы React работал так, разработчики сделали React похожим на функциональное программирование. Правило большого пальца функционального программирования неизменность. Позвольте мне объяснить это громко и ясно.

Как работает однонаправленный поток?

  • states - это хранилище данных, которое содержит данные компонента.
  • view компонента отображает на основе состояния.
  • Когда view необходимо что-то изменить на экране, это значение должно быть предоставлено из store.
  • Чтобы это произошло, React предоставляет функцию setState(), которая принимает object нового states и выполняет сравнение и слияние (аналогично object.assign()) по сравнению с предыдущим состоянием и добавляет новое состояние в хранилище данных состояния.
  • Всякий раз, когда данные в хранилище состояний меняются, реакция вызывает повторную визуализацию с новым состоянием, которое view потребляет и показывает его на уровне.

Этот цикл будет продолжаться в течение всего времени жизни компонента.

Если вы видите вышеуказанные шаги, это ясно показывает, что многое происходит, когда вы меняете состояние. Итак, когда вы напрямую мутируете состояние и вызываете setState() с пустым объектом. previous state будет загрязнен вашей мутацией. В связи с этим, мелкое сравнение и слияние двух государств будет нарушено или не произойдет, потому что сейчас у вас будет только одно государство. Это нарушит все методы жизненного цикла React.

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

И еще один недостаток мутации Objects и Arrays в JavaScript - это когда вы назначаете объект или массив, вы просто делаете ссылку на этот объект или этот массив. Когда вы их мутируете, будет затронута вся ссылка на этот объект или этот массив. React обрабатывает это разумным способом в фоновом режиме и просто дает нам API, чтобы заставить его работать.

Наиболее распространенные ошибки, возникающие при обработке состояний в реакции

//original state
this.state = {
   a: [1,2,3,4,5]
}

//changing the state in react
//need to add '6' in the array

//bad approach
const b = this.state.a.push(6)
this.setState({
  a: b
}) 

В приведенном выше примере this.state.a.push(6) будет мутировать состояние напрямую. Присвоение его другой переменной и вызов setState аналогичны приведенному ниже. Так как мы все равно мутировали состояние, нет никакой точки, назначающей ее другой переменной и вызывающей setState с этой переменной.

 //same as 
 this.state.a.push(6)
 this.setState({})

Большинство людей это делает. Это так неправильно. Это нарушает красоту Реакта, и это сделает вас плохим программистом.

Итак, как лучше всего реагировать на состояния в реакции? Позвольте мне объяснить.

Когда вам нужно изменить "что-то" в существующем состоянии, сначала получите копию этого "чего-то" из текущего состояния.

//original state
    this.state = {
       a: [1,2,3,4,5]
    }

 //changing the state in react
 //need to add '6' in the array

 //create a copy of this.state.a
 //you can use ES6 destructuring or loadash _.clone()
 const currentStateCopy = [...this.state.a]

Теперь мутация currentStateCopy не будет мутировать исходное состояние. Выполняйте операции над currentStateCopy и установите его как новое состояние с помощью setState()

currentStateCopy.push(6)
this.state({
 a: currentStateCopy
})

Это правильно украсить?

Таким образом, все ссылки this.state.a не будут затронуты, пока мы не будем использовать setState. Это дает вам контроль над вашим кодом, и это поможет вам написать элегантный тест и сделать вас уверенным в производительности кода в процессе производства.

Чтобы ответить на ваш вопрос,

Почему я не могу напрямую изменить состояние компонента?


Да, вы можете. Но вам нужно столкнуться с следующими последствиями.

  • Когда вы масштабируете, вы будете писать неуправляемый код.
  • Вы потеряете контроль state между компонентами.
  • Вместо того, чтобы использовать React, вы будете писать собственные коды над React.

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

PS. Я написал около 10000 строк изменчивого кода React js. Если он ломается сейчас, я не знаю, где искать, потому что все значения мутируют где-то. Когда я это осознал, я начал писать неизменный код. Доверьтесь мне! Это лучшее, что вы можете сделать с продуктом или приложением.

Надеюсь, это поможет!

Ответ 3

самый простой ответ на "

Почему я не могу напрямую изменить состояние компонента:

это все о фазе обновления.

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

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

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

Затем "Реакция" выяснит, что изменилось, и на основании этого она соответствующим образом обновит реальный DOM.

Надеюсь, поможет.

Ответ 4

Мое настоящее понимание основано на этом и этом:

IF вы не используете shouldComponentUpdate или любые другие методы жизненного цикла (например, componentWillReceiveProps, componentWillUpdate и componentDidUpdate)), где вы сравниваете старый и новый реквизит/состояние

THEN

его штраф, чтобы мутировать state, а затем вызвать setState(), иначе это не нормально.