Обновление состояния с помощью реквизита для дочернего компонента React
У меня есть приложение React, где реквизит из родительского компонента передается дочернему компоненту, а реквизиты затем устанавливают состояние для дочернего элемента.
После отправки обновленного значения родительскому компоненту дочерний компонент не обновляет состояние с обновленными реквизитами.
Как мне получить его для обновления состояния дочернего компонента?
Мой сокращенный код:
class Parent extends React.Component {
constructor (props) {
super(props);
this.state = {name: ''}
}
componentDidMount () {
this.setState({name: this.props.data.name});
}
handleUpdate (updatedName) {
this.setState({name: updatedName});
}
render () {
return <Child name={this.state.name} onUpdate={this.handleUpdate.bind(this)} />
}
}
class Child extends React.Component {
constructor (props) {
super(props);
this.state = {name: ''}
}
componentDidMount () {
this.setState({name: this.props.name});
}
handleChange (e) {
this.setState({[e.target.name]: e.target.value});
}
handleUpdate () {
// ajax call that updates database with updated name and then on success calls onUpdate(updatedName)
}
render () {
console.log(this.props.name); // after update, this logs the updated name
console.log(this.state.name); // after update, this logs the initial name until I refresh the brower
return <div>
{this.state.name}
<input type="text" name="name" value={this.state.name} onChange={this.handleChange} />
<input type="button" value="Update Name" onClick={this.handleUpdate.bind(this)} />
</div>
}
}
Ответы
Ответ 1
Вам нужно реализовать componentWillReceiveProps
в вашем ребенке:
componentWillReceiveProps(newProps) {
this.setState({name: newProps.name});
}
Изменить: componentWillReceiveProps
теперь устарела и будет удалена, но в ссылке на документацию выше есть альтернативные предложения.
Ответ 2
Вызов setState()
в компонентеWillReceiveProps не вызывает дополнительной повторной обработки. Прием реквизита - это один рендер, и this.setState будет другим рендером, если он был выполнен в рамках метода, такого как componentDidUpdate. Я бы порекомендовал сделать this.state.name !== nextProps.name
в файле mustComponentUpdate, чтобы он всегда проверялся на любое обновление.
componentWillReceiveProps(nextProps) {
this.setState({name: nextProps.name});
}
shouldComponentUpdate(nextProps) {
return this.state.name !== nextProps.name;
}
Ответ 3
Также было бы неплохо проверить, нужно ли даже обновлять состояние, так как это приведет к повторной обработке.
componentWillReceiveProps(newProps) {
if (this.state.name !== newProps.name) {
this.setState({name: newProps.name});
}
}
Ответ 4
Пара вещей. То, как вы связываете функции при нажатии, необычно, если не сказать больше. Я бы посоветовал вам либо сделать это в конструкторе, либо использовать вместо него функцию стрелки (это автоматически свяжет функцию с классом).
export default class Parent extends Component {
constructor (props) {
super(props);
this.state = {name: ''}
}
handleUpdate = (updatedName) => {
this.setState({name: updatedName})
}
render () {
return <Child name={this.state.name} onUpdate={this} />
}
}
export default class Child extends React.Component {
constructor(props) {
super(props);
this.state = { name: "" };
}
componentDidMount() {
this.setState({ name: this.props.name });
}
handleChange = (e) => {
this.setState({ name: e.target.value });
}
handleUpdate() {
// ajax call that updates database with updated name and then on success calls onUpdate(updatedName)
}
render() {
console.log(this.props.name); // after update, this logs the updated name
console.log(this.state.name); // after update, this logs the initial name until I refresh the brower
return (
<div>
{this.state.name}
<input
type="text"
name="name"
value={this.state.name}
onChange={this.handleChange}
/>
<input
type="button"
value="Update Name"
onClick={this.handleUpdate}
/>
</div>
);
}
}
Кроме того, я бы посоветовал вам решить, нужно ли управлять/обновлять реквизиты от Parent или Child. Если Parent отвечает за обработку состояния, тогда вы должны передать handleUpdate в Parent:
//@Parent component
<Child handleUpdate={()=> this.handleUpdate} .../>
//@Child component
handleUpdate = () => {
this.props.handleUpdate
}
От вас не требуется использовать любую другую функцию (в React 16+) для управления реквизитом от ребенка к родителю и наоборот.
Обычно эти "двунаправленные" случаи являются структурными "запахами" и означают, что разделение проблем для каждого компонента было неправильно скорректировано или еще не полностью выяснено.