Реактивний дочірній компонент не оновлюється після зміни батьківського стану


109

Я намагаюся зробити хороший компонент ApiWrapper для заповнення даних у різних дочірніх компонентах. З усього, що я прочитав, це має працювати: https://jsfiddle.net/vinniejames/m1mesp6z/1/

class ApiWrapper extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      response: {
        "title": 'nothing fetched yet'
      }
    };
  }

  componentDidMount() {
    this._makeApiCall(this.props.endpoint);
  }

  _makeApiCall(endpoint) {
    fetch(endpoint).then(function(response) {
      this.setState({
        response: response
      });
    }.bind(this))
  }

  render() {
    return <Child data = {
      this.state.response
    }
    />;
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: props.data
    };
  }

  render() {
    console.log(this.state.data, 'new data');
    return ( < span > {
      this.state.data.title
    } < /span>);
  };
}

var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;

ReactDOM.render(
  element,
  document.getElementById('container')
);

Але чомусь здається, що дочірній компонент не оновлюється, коли змінюється стан батьків.

Я щось тут пропускаю?

Відповіді:


205

З кодом є дві проблеми.

Початковий стан вашого дочірнього компонента встановлюється з реквізиту.

this.state = {
  data: props.data
};

Цитуючи цей відповідь ТА :

Передача стаціонарного стану компоненту як a propє антидіаграмою, оскільки getInitialStateметод (у нашому випадку конструктор) викликається лише вперше, коли компонент видає. Ніколи більше. Це означає, що якщо ви повторно візуалізуєте цей компонент, передаючи інше значення як a prop, компонент не реагуватиме відповідно, оскільки компонент буде зберігати стан з першого разу, коли він був наданий. Це дуже схильне до помилок.

Тож якщо ви не можете уникнути такої ситуації, ідеальним рішенням є використання методу componentWillReceivePropsдля прослуховування нових реквізитів.

Додавання нижче коду до дочірнього компонента вирішить вашу проблему з повторним наданням дочірнього компонента.

componentWillReceiveProps(nextProps) {
  this.setState({ data: nextProps.data });  
}

Другий випуск - з fetch.

_makeApiCall(endpoint) {
  fetch(endpoint)
    .then((response) => response.json())   // ----> you missed this part
    .then((response) => this.setState({ response }));
}

А ось робоча скрипка: https://jsfiddle.net/o8b04mLy/


1
"Непогано ініціалізувати стан на основі реквізиту, якщо ви знаєте, що ви робите" Чи є інші недоліки встановлення стану через опору, окрім того, що nextPropне буде спровоковано повторне відображення без componentWillReceiveProps(nextProps)?
Вінні Джеймс

11
Наскільки я знаю, інших недоліків немає. Але у вашому випадку ми могли б чітко уникнути наявності стану в дочірніх компонентах. Батько може просто передавати дані у вигляді реквізиту, а коли батьків повторно відображається зі своїм новим станом, дитина також повторно візуалізує (з новими реквізитами). Насправді збереження стану всередині дитини тут непотрібне. Чисті компоненти FTW!
Ядху Кіран

7
Для всіх, хто читає відтепер, перевірте static getDerivedStateFromProps(nextProps, prevState) reactjs.org/docs/…
GoatsWearHats

4
Чи є якесь інше рішення для цього, окрім компонентаWillReceiveProps (), оскільки воно зараз застаріле?
LearningMath

3
@LearningMath, будь ласка, ознайомтеся з останніми документами React, де йдеться про альтернативні способи. Можливо, вам доведеться переглянути свою логіку.
Ядху Кіран

1

Є кілька речей, які потрібно змінити.

Коли fetchотримаєте відповідь, це не json. Я шукав, як можна отримати цей json, і я виявив це посилання .

З іншого боку, вам потрібно подумати, що constructorфункція викликається лише один раз.

Отже, вам потрібно змінити спосіб отримання даних у <Child>компоненті.

Тут я залишив приклад коду: https://jsfiddle.net/emq1ztqj/

Я сподіваюся, що це допомагає.


Дякую. Хоча, дивлячись на приклад, який ви зробили, все ще здається, що дитина ніколи не оновлюється. Будь-які пропозиції щодо того, як змінити спосіб отримання дитиною даних?
Вінні Джеймс

2
Ти впевнений? Я бачу, що цей <Child>компонент витягує нові дані https://jsonplaceholder.typicode.com/posts/1та робить їх повторно.
slorenzo

1
Так, я бачу, що він працює зараз на OSX. iOS не викликав повторну візуалізацію в браузері додатків StackExchange
Вінні Джеймс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.