TLDR: Общие требования к конструкции, по-видимому, невозможны в Реагировании без нарушения Принципов реагирования или игнорирования стандартных возможностей инкапсуляции.Изменения в управляемых компонентах формы в реактиве
У меня есть форма, которая отображает существующие данные из API-интерфейса сервера и позволяет пользователю редактировать поля и обновлять данные на сервере. Это происходит динамически, так как пользователь меняет входные значения формы, а не требует, чтобы они «отправляли» форму после каждого редактирования.
Для некоторых входных форматов изменение значения производится немедленно (например, флажок, переключатель, выбор). Для других изменение значения производится постепенно - наиболее очевидно для ввода текста. Я бы не хотел, чтобы на каждом нажатии клавиши был запрос на сервер, так как это приведет к ошибкам проверки на стороне сервера, которые генерируются для неполных значений. Вместо этого сервер будет обновляться после того, как пользователь покинет поле ввода текста. Было бы также напрасно отправлять запрос сервера, если значения не изменяются.
В Реагировании ключевым принципом дизайна является то, что вы поддерживаете единственный источник истины/состояния и прокладываете это через компоненты с помощью реквизита. Компоненты не должны сохранять свое собственное состояние как копию реквизита, но вместо этого должны оказывать реквизиты непосредственно [1]. «Единственным источником» будет родительский компонент, который извлекает и передает данные с сервера и на сервер.
Для вынесенного <input>
элемента, чтобы сохранить свое значение в курс изменений сервера, он должен использовать атрибут value
, так как defaultValue
вычисляется только на первую визуализацию [2]. Принципы проектирования React подразумевают, что я должен установить <input value={this.props.value} />
. Чтобы это отвечало на ввод пользователя, должен быть также предоставлен обработчик onChange
, барботирующий изменение до родительского компонента, которое обновит состояние и приведет к повторной обработке <input>
с обновленным props
.
Тем не менее, я бы не хотел запускать запрос сервера в обработчике onChange
, так как это будет срабатывать при каждом нажатии клавиши. Мне нужно было бы вызвать запрос сервера вместо этого на событие onBlur
, предположив, что значение изменилось с onFocus
. Требование этого для некоторых элементов, а не для других означает, что родительскому компоненту понадобятся два обработчика: обработчик onChange
для обновления состояния для всех дочерних компонентов и запуска запроса сервера для определенных полей и onBlur
для запуска запроса сервера для других полей , Требование, чтобы родительский компонент знал, какой из компонентов дочерней формы должен показать, какое поведение кажется неудачным для инкапсуляции должным образом. Детальные компоненты должны иметь возможность контролировать свои собственные значения и решать, когда следует генерировать событие «сделать что-то».
Я не вижу способа достижения этого, не нарушая одного из принципов React, скорее всего, поддерживая состояние в каждом компоненте формы. Что-то вроде этого:
class TextInput extends React.Component {
constructor(props) {
super(props);
this.initialValue = this.props.value;
this.setState({value: this.props.value});
}
componentWillReceiveProps = (nextProps) => {
this.initialValue = nextProps.value;
this.setState({value: nextProps.value});
};
handleFocus = (e) => {
this.initialValue = e.target.value;
};
handleChange = (e) => {
this.setState({value: e.target.value});
};
handleBlur = (e) => {
if (e.target.value !== this.initialValue &&
this.props.handleChange) {
this.props.handleChange(e);
}
};
render() {
return (
<input type="text"
value={this.state.value}
onFocus={this.handleFocus}
onChange={this.handleChange}
onBlur={this.handleBlur} />
);
}
}
class FormHandler extends React.Component {
componentDidMount() {
// fetch from API...
this.setState(apiResponse);
}
handleChange = (e) => {
// update API with e.target.value....
};
render() {
return (<TextInput value={this.state.value}
handleChange={this.handleChange} />);
}
}
Есть ли лучший способ достижения этой цели, не нарушая принципы РЕАКТА о капельном реквизита вниз, чтобы быть оказано?
Дальнейшее чтение различных попыток разрешить это в [3-4].
Другие вопросы SO от людей, борющихся с аналогичной проблемой в [5-6].
[1] https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
[2] https://facebook.github.io/react/docs/forms.html#default-value
[3] https://discuss.reactjs.org/t/how-to-pass-in-initial-value-to-form-fields/869
[4] https://blog.iansinnott.com/managing-state-and-controlled-form-fields-with-react/
[5] How do I reset the defaultValue for a React input
[6] Using an input field with onBlur and a value from state blocks input in Reactjs JSX?
Я не уверен, что это не та же проблема, только с расположением методов, перемещаемых до FormHandler. FormHandler все еще устанавливает свое внутреннее состояние из переданных ему реквизитов (предположительно React no-no). – practual
Ах, право. Кажется, я пропустил это. Я бы удалил, где я сказал начальное состояние значения === 'this.props.value'. Я предполагаю, что значение исходит из API, поэтому я бы просто установил его по умолчанию и затем обновил его, как только вызов API происходит внутри 'compnentDidMount'. – Dave