Инициализация контролируемого поля после построения

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

Насколько я понимаю, даже если вы выполните setState, который вызывает повторную визуализацию, это не изменит значение элемента управления - хотя я не уверен, почему.

Итак, следующее не будет работать:

componentDidMount() {

   ...   // code to get value

   this.setState({ value: "hello world" });
   return ( <input type="text" value={this.state.value} onChange={this.handleChange} />
}

Вместо этого я попытался отобразить ввод в обратном вызове setState, чтобы убедиться, что значение установлено:

componentDidMount() {

   ...   // code to get value

   this.setState({ value: "hello world" }, this.renderInput);
}

renderInput = () => {
   this.input = ( <input type="text" value={this.state.value} onChange={this.handleChange} />
}

Примечание: моя форма включает {this.input)

Во втором примере моя форма отображается идеально, и ввод инициализируется. Однако потом вызывается функция onChange, хоть она и корректно обновляет состояние, изменение состояния не подхватывается — у меня поле ввода зависает, как будто onChange вообще не было.

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


person Ben    schedule 12.04.2018    source источник
comment
Почему вы возвращаете <input> от componentDidMount()? Что делает ваш this.handleChange? Вы можете включить свою функцию render()?   -  person Pipe    schedule 12.04.2018
comment
Согласен, я не думаю, что возврат элемента из componentDidMount когда-либо что-то даст.   -  person ecraig12345    schedule 13.04.2018
comment
Также нам определенно нужно увидеть handleChange, так как, вероятно, проблема именно в этом.   -  person ecraig12345    schedule 13.04.2018


Ответы (1)


РЕДАКТИРОВАТЬ: при повторном прочтении я понял, что ваш код эффективно делает то же самое, что предложил мой первоначальный ответ (но очень неидиоматично). Вероятно, настоящая проблема заключается в вашем handleChange, но я оставлю исходный ответ, так как он показывает более идиоматический способ ожидания рендеринга поля.


(оригинальный ответ)

Самым простым решением было бы не отображать <input>, пока вы не узнаете его начальное значение. Используйте что-то вроде этого в соответствующем месте в render():

{ typeof this.state.value === 'string' ?
  <input type="text" value={this.state.value} onChange={this.handleChange} /> :
  null }

Недостаток такого подхода: если в форме есть другие элементы, то они будут "прыгать" при появлении этого поля, что не очень хорошо.

Чтобы избежать скачков, вы можете подождать с отрисовкой любого элемента управления формы, пока не будут доступны все начальные значения. Добавьте свойство this.state.isLoading, которое изначально имеет значение true, а затем устанавливается в false по окончании загрузки. (Для удобства в примере кода предполагается, что у вас есть функция getInitialValue(), которая возвращает promise, но вы можете сделать что-то подобное с обратными вызовами или какой-либо другой конструкцией.)

componentDidMount() {
  getInitialValue().then((initialValue) => {
    this.setState({ value: initialValue, isLoading: false });
  });
}

render() {
  if (this.state.isLoading)
    return null; // or something like <div>Loading...</div>

  // else, return normal stuff
}
person ecraig12345    schedule 13.04.2018