У меня возникают проблемы с формой React и правилом управления государством. У меня есть поле ввода времени в форме (в модальном). Начальное значение устанавливается как переменная состояния в getInitialState
и передается из родительского компонента. Это само по себе прекрасно работает.
Проблема возникает, когда я хочу обновить значение start_time по умолчанию через родительский компонент. Само обновление происходит в родительском компоненте через setState start_time: new_time
. Однако в моей форме значение start_time по умолчанию никогда не изменяется, поскольку оно определяется только один раз в getInitialState
.
Я попытался использовать componentWillUpdate
для принудительного изменения состояния через setState start_time: next_props.start_time
, который действительно работал, но дал мне ошибки Uncaught RangeError: Maximum call stack size exceeded
.
Итак, мой вопрос: какой правильный способ обновления состояния в этом случае? Думаю ли я об этом неправильно?
Текущий код:
@ModalBody = React.createClass
getInitialState: ->
start_time: @props.start_time.format("HH:mm")
#works but takes long and causes:
#"Uncaught RangeError: Maximum call stack size exceeded"
componentWillUpdate: (next_props, next_state) ->
@setState(start_time: next_props.start_time.format("HH:mm"))
fieldChanged: (fieldName, event) ->
stateUpdate = {}
stateUpdate[fieldName] = event.target.value
@setState(stateUpdate)
render: ->
React.DOM.div
className: "modal-body"
React.DOM.form null,
React.createElement FormLabelInputField,
type: "time"
id: "start_time"
label_name: "Start Time"
value: @state.start_time
onChange: @fieldChanged.bind(null, "start_time")
@FormLabelInputField = React.createClass
render: ->
React.DOM.div
className: "form-group"
React.DOM.label
htmlFor: @props.id
@props.label_name + ": "
React.DOM.input
className: "form-control"
type: @props.type
id: @props.id
value: @props.value
onChange: @props.onChange
Если я правильно понимаю, у вас есть родительский компонент, который передает start_time
до компонента ModalBody
, который присваивает его своему собственному состоянию? И вы хотите обновить это время от родителя, а не от дочернего компонента.
У React есть несколько советов по работе с этим сценарием. (Обратите внимание, что это старая статья, которая с тех пор была удалена из Интернета. ссылка на текущий doc на компонентные реквизиты).
Использование реквизита для генерации состояния в
getInitialState
часто приводит к дублированию "источника истины", то есть к реальным данным. Это связано с тем, чтоgetInitialState
вызывается только при первом создании компонента.По возможности, вычисляйте значения "на лету", чтобы они не выходили из синхронизации позже и вызывают проблемы с обслуживанием.
В принципе, всякий раз, когда вы назначаете родительский props
дочернему элементу state
, метод рендеринга не всегда вызывается при обновлении prop. Вы должны вызвать его вручную, используя метод componentWillReceiveProps
.
componentWillReceiveProps(nextProps) {
// You don't have to do this check first, but it can help prevent an unneeded render
if (nextProps.startTime !== this.state.startTime) {
this.setState({ startTime: nextProps.startTime });
}
}
Видимо, все меняется.... getDerivedStateFromProps() теперь является предпочтительной функцией.
class Component extends React.Component {
static getDerivedStateFromProps(props, current_state) {
if (current_state.value !== props.value) {
return {
value: props.value,
computed_prop: heavy_computation(props.value)
}
}
return null
}
}
null
если ничего не должно измениться сразу после вашего if, вы должны пойти с return null
Также есть компонент componentDidUpdate.
Функция подписи:
componentDidUpdate(prevProps, prevState, snapshot)
Используйте это как возможность работать на DOM, когда компонент был обновлен. Не вызывается при начальном render
.
Увидимся, вероятно, вам не нужна статья о производном состоянии, которая описывает Anti-Pattern как для componentDidUpdate
и для getDerivedStateFromProps
. Я нахожу это очень полезным.
componentWillReceiveProps
устарела, потому что его использование "часто приводит к ошибкам и несоответствиям".
Если что-то меняется извне, рассмотрите возможность полного сброса дочернего компонента с помощью key
.
Предоставление key
подпорки дочернему компоненту гарантирует, что всякий раз, когда значение key
изменяется извне, этот компонент перерисовывается. Например,
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
По его производительности:
Хотя это может звучать медленно, разница в производительности, как правило, незначительна. Использование ключа может быть даже быстрее, если компоненты имеют сложную логику, которая запускается при обновлении, поскольку diffing обходится для этого поддерева.
Из реагирующей документации: https://reactjs.org/blog/2018/06/07/you-probbly-dont-need-derived-state.html
Стирание состояния при смене реквизита - это анти-паттерн
Начиная с версии 16, componentWillReceiveProps устарела. Из реактивной документации, рекомендуемый подход в этом случае является использование
ParentComponent
из ModalBody
будет владеть состоянием start_time
. Это не мой предпочтительный подход в этом случае, так как я думаю, что модал должен владеть этим состоянием.start_time
из вашего ModalBody
и использовали бы getInitialState
как вы уже сделали. Чтобы сбросить состояние start_time
, вы просто меняете ключ из ParentComponent
Возможно, вам не нужно производное состояние
1. Установить ключ от родителя
При изменении ключа React создаст новый экземпляр компонента, а не обновит текущий. Ключи обычно используются для динамических списков, но также полезны здесь.
2. Используйте getDerivedStateFromProps
/componentWillReceiveProps
Если по какой-то причине ключ не работает (возможно, компонент очень дорог для инициализации)
Используя getDerivedStateFromProps
вы можете сбросить любую часть состояния, но в настоящее время она выглядит немного глючной (v16.7) !, см. Ссылку выше для использования.
Это вполне понятно из их документов:
If you used componentWillReceiveProps for re-computing some data only when a prop changes, use a memoization helper instead.
Используйте: https://reactjs.org/blog/2018/06/07/you-probbly-dont-need-derived-state.html#what-about-memoization