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

94

Иногда у меня есть компоненты реакции, которые концептуально сохраняют состояние, и я хочу их сбросить. Идеальное поведение было бы эквивалентно удалению старого компонента и чтению нового, нетронутого компонента.

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

Есть ли для этого API или шаблон?

Изменить: я сделал тривиальный пример, демонстрирующий this.replaceState(this.getInitialState())подход и противопоставляющий его this.setState(this.getInitialState())подходу: jsfiddle - replaceStateболее надежен.

Эамон Нербонн
источник

Ответы:

211

Чтобы гарантировать, что указанное вами неявное состояние браузера и состояние дочерних элементов сброшены, вы можете добавить keyатрибут к компоненту корневого уровня, возвращаемому render; когда он изменится, этот компонент будет выброшен и создан с нуля.

render: function() {
    // ...
    return <div key={uniqueId}>
        {children}
    </div>;
}

Нет ярлыка для сброса локального состояния отдельного компонента.

Софи Альперт
источник
1
Просто из любопытства, когда вы говорите «компонент будет выброшен и создан с нуля», вы имеете в виду, что виртуальный DOM будет выброшен и создан с нуля, но обновления DOM все равно будут происходить на основе алгоритма сравнения?
nimgrg
1
@nimgrg Нет, реальные элементы DOM также будут воссозданы. (Виртуальный DOM уже воссоздается при каждом рендеринге, поэтому, если вы хотите сохранить реальный DOM, просто не устанавливайте a keyв этом случае.)
Софи Альперт,
3
@EamonNerbonne В React 0.8 ключи используются только для различения одноуровневых элементов в массиве и игнорируются в корне. См. Github.com/facebook/react/issues/590 .
Софи Альперт
Обратите внимание, что replaceState()и getInitialState()оба являются устаревшими в новых реакциях в стиле классов ES6. this.setState(this._getInitialState())Вместо этого используйте . Также вы не можете назвать свою собственную функцию инициализатора состояния getInitialState()- response выдает предупреждение - назовите ее как-нибудь еще и явно сделайте это state = this._getInitialState()на верхнем уровне класса.
thom_nic
2
Есть ли способ сделать это изнутри компонента, не требуя извне передавать ключевую опору?
trusktr
14

На самом деле вам следует избегать replaceStateи использовать setStateвместо этого.

В документации сказано, что replaceState«может быть полностью удалено в будущей версии React». Я думаю, что он определенно будет удален, потому что replaceStateон не соответствует философии React. Это способствует тому, что компонент React начинает казаться чем-то вроде швейцарского ножа. Это препятствует естественному росту компонента React, который становится меньше и более специализированным.

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

Во всяком случае, вот как вы это делаете. Подобно (принятому) ответу Бена выше, но вот так:

this.setState(this.getInitialState());

Также (как сказал Бен), чтобы сбросить «состояние браузера», вам нужно удалить этот узел DOM. Используйте всю мощь vdom и используйте новую keyопору для этого компонента. Новый рендер полностью заменит этот компонент.

Ссылка: https://facebook.github.io/react/docs/component-api.html#replacestate

wle8300
источник
2
Это не сработает, если текущее состояние включает какие-либо свойства, которые не находятся в исходном состоянии. Хотя я не верю, что это хорошее «состояние», если вы не можете каким-то образом предотвратить это, я бы хотел избежать неожиданной поломки. Я был бы в порядке со сбросом, который в некоторых случаях дает сбой , но не с сбросом, который слегка искажает состояние.
Eamon Nerbonne
Не уверен, что понимаю. Вы говорите, что это не эквивалент this.replaceState? Потому что это так.
wle8300
1
@ williamle8300 Они не были бы эквивалентны, если бы новое свойство добавлялось в состояние где-то в жизненном цикле компонента между вызовами getInitialState()(скажем, в обработчике onClick). В этом случае это новое свойство все еще будет присутствовать после 2-го getInitialState().
Грэм
@Graham Я считаю плохой практикой вводить новое состояние в компонент, который еще не объявлен в getInitialState. Это похоже на объявление всех ваших переменных в функции JS в верхней части функции. Боковое примечание: решение Бена Альперта почти идентично моему ... и он официальный сопровождающий React!
wle8300
1
Боюсь, что функция getInitialState () и, следовательно, ответ устарели. Было бы неплохо обновить.
Мартин Р.
10

Добавление keyатрибута к элементу, который необходимо повторно инициализировать, будет перезагружать его каждый раз при изменении элемента propsили stateсвязывания с ним.

ключ = {новая дата (). getTime ()}

Вот пример:

render() {
  const items = (this.props.resources) || [];
  const totalNumberOfItems = (this.props.resources.noOfItems) || 0;

  return (
    <div className="items-container">
      <PaginationContainer
        key={new Date().getTime()}
        totalNumberOfItems={totalNumberOfItems}
        items={items}
        onPageChange={this.onPageChange}
      />
    </div>
  );
}
jsbisht
источник
1
Или вы можете добавить в качестве ключа простую целочисленную переменную-счетчик
минималистский