Обновление состояния с помощью реквизита для дочернего компонента 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+) для управления реквизитом от ребенка к родителю и наоборот.

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