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

Обновление состояния изменения реквизита в форме отзыва

У меня возникают проблемы с формой 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
4b9b3361

Ответ 1

Если я правильно понимаю, у вас есть родительский компонент, который передает 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 });
  }
}

Ответ 2

Видимо, все меняется.... 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
  }
}

Ответ 3

Также есть компонент componentDidUpdate.

Функция подписи:

componentDidUpdate(prevProps, prevState, snapshot)

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

Увидимся, вероятно, вам не нужна статья о производном состоянии, которая описывает Anti-Pattern как для componentDidUpdate и для getDerivedStateFromProps. Я нахожу это очень полезным.

Ответ 4

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

Если что-то меняется извне, рассмотрите возможность полного сброса дочернего компонента с помощью key.

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

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

По его производительности:

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

Ответ 5

Из реагирующей документации: https://reactjs.org/blog/2018/06/07/you-probbly-dont-need-derived-state.html

Стирание состояния при смене реквизита - это анти-паттерн

Начиная с версии 16, componentWillReceiveProps устарела. Из реактивной документации, рекомендуемый подход в этом случае является использование

  1. Полностью контролируемый компонент: ParentComponent из ModalBody будет владеть состоянием start_time. Это не мой предпочтительный подход в этом случае, так как я думаю, что модал должен владеть этим состоянием.
  2. Полностью неконтролируемый компонент с ключом: это мой предпочтительный подход. Пример из реагирующей документации: https://codesandbox.io/s/6v1znlxyxn. Вы бы полностью владели состоянием start_time из вашего ModalBody и использовали бы getInitialState как вы уже сделали. Чтобы сбросить состояние start_time, вы просто меняете ключ из ParentComponent

Ответ 7

Возможно, вам не нужно производное состояние

1. Установить ключ от родителя

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

2. Используйте getDerivedStateFromProps/componentWillReceiveProps

Если по какой-то причине ключ не работает (возможно, компонент очень дорог для инициализации)

Используя getDerivedStateFromProps вы можете сбросить любую часть состояния, но в настоящее время она выглядит немного глючной (v16.7) !, см. Ссылку выше для использования.

Ответ 8

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

class Company extends XComponent {
    constructor(props) {
        super(props);
        this.data = {};
    }
    fetchData(data) {
        this.resetState(data);
    }
    render() {
        return (
            <Input ref={c => this.data['name'] = c} type="text" className="form-control" />
        );
    }
}
class XComponent extends Component {
    resetState(obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property) && typeof this.data[property] !== 'undefined') {
                if ( obj[property] !== this.data[property].state.value )
                    this.data[property].setState({value: obj[property]});
                else continue;
            }
            continue;
        }
    }
}

Ответ 9

Новый способ перехвата состоит в том, чтобы использовать useEffect вместо 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 });
  }
}

в функциональном компоненте, управляемом компонентами, становится следующим:

// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
// 
useEffect(() => {
  if (props.startTime !== startTime) {
    setStartTime(props.startTime);
  }
}, [props.startTime]);

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