Я пытаюсь создать хороший компонент 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 ;
  }
}

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')
);

Но почему-то кажется, что дочерний компонент не обновляется при изменении родительского состояния.

Я что-то упустил?

Vinnie James

Ответов: 3

Ответы (3)

Есть две проблемы с вашим кодом.

Начальное состояние вашего дочернего компонента устанавливается из props.

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

Цитата из этого Ответ SO:

Передача начального состояния компоненту как prop является анти-паттерном потому что метод getInitialState (в нашем случае конструктор) вызывается только в первый раз, когда компонент рендеринга. Никогда больше. Это означает, что если вы повторно визуализируете это компонент, передающий другое значение как 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/

Есть кое-что, что вам нужно изменить.

Когда fetch получает ответ, это не json. Я искал, как мне получить этот json, и обнаружил эту ссылку.

С другой стороны, вы должны думать, что функция constructor вызывается только один раз.

Итак, вам нужно изменить способ получения данных в компоненте .

Здесь я оставил пример кода: https://jsfiddle.net/emq1ztqj/

Надеюсь, это поможет.

Если приведенное выше решение все еще не решило вашу проблему, я предлагаю вам один раз увидеть, как вы меняете состояние, если вы не возвращаете новый объект, тогда иногда реакция не видит разницы в новом предыдущем и измененном состояние, рекомендуется всегда передавать новый объект при изменении состояния, поскольку реакция нового объекта определенно приведет к повторному рендерингу всех необходимых компонентов, которые имеют доступ к этому измененному состоянию.

Например: -

Здесь я изменю одно свойство массива объектов в моем состоянии, посмотрим, как я распределяю все данные в новом объекте. Кроме того, приведенный ниже код может показаться вам немного чуждым, это функция редуктора, НО не волнуйтесь, это просто метод изменения состояния.

export const addItemToCart = (cartItems,cartItemToBeAdded) => {
        return cartItems.map(item => {
            if(item.id===existingItem.id){
                ++item.quantity;        
            }
            // I can simply return item but instead I spread the item and return a new object
            return {...item} 
        })
    } 

Просто убедитесь, что вы меняете состояние с новым объектом, даже если вы сделаете небольшое изменение в состоянии, просто распределите его в новом объекте, а затем верните, это вызовет рендеринг во всех подходящих местах. Надеюсь, это помогло. Дайте знать, если я где-то ошибаюсь :)

2022 WebDevInsider