Подтвердить что ты не робот

Регенерация дочернего регенератора в React.js

Компонент Parent (MyList в моем примере) отображает массив через MyComponent компонент (MyComponent). Родитель решает изменить свойства в массиве. Что такое React-способ запуска дочернего повторного рендеринга?

Все, что я придумал, это this.setState({}); в родительской после настройки данных. Это хак или React способ запуска обновления?

JS Fiddle: https://jsfiddle.net/69z2wepo/7601/

var items = [
  {id: 1, highlighted: false, text: "item1"},
  {id: 2, highlighted: true, text: "item2"},
  {id: 3, highlighted: false, text: "item3"},
];

var MyComponent = React.createClass({
  render: function() {
    return <div className={this.props.highlighted ? 'light-it-up' : ''}>{this.props.text}</div>;
  }
});

var MyList = React.createClass({
  toggleHighlight: function() {
    this.props.items.forEach(function(v){
      v.highlighted = !v.highlighted;
    });

    // Children must re-render
    // IS THIS CORRECT?
    this.setState({});
  },

  render: function() {
    return <div>
      <button onClick={this.toggleHighlight}>Toggle highlight</button>
      {this.props.items.map(function(item) {
          return <MyComponent key={item.id} text={item.text} highlighted={item.highlighted}/>;
      })}
    </div>;
  }
});

React.render(<MyList items={items}/>, document.getElementById('container'));
4b9b3361

Ответ 1

Проблема заключается в том, что вы сохраняете состояние в this.props вместо this.state. Поскольку этот компонент мутирует items, items является состоянием и должен храниться в this.state. (Здесь хорошая статья о реквизитах против состояния.) Это решает проблему рендеринга, потому что когда вы обновляете items, вы вызываете setState, что автоматически вызвать повторную визуализацию.

Здесь ваш компонент будет выглядеть с использованием состояния вместо реквизита:

var MyList = React.createClass({
    getInitialState: function() {
        return { items: this.props.initialItems };
    },

    toggleHighlight: function() {
        var newItems = this.state.items.map(function (item) {
            item.highlighted = !item.highlighted;
            return item;
        });

        this.setState({ items: newItems });
    },

    render: function() {
        return (
            <div>
                <button onClick={this.toggleHighlight}>Toggle highlight</button>
                { this.state.items.map(function(item) {
                    return <MyComponent key={item.id} text={item.text} 
                             highlighted={item.highlighted}/>;
                }) }
            </div>
        );    
    }
});

React.render( <MyList initialItems={initialItems}/>,
              document.getElementById('container') );

Обратите внимание, что я переименовал items prop в initialItems, потому что он ясно показывает, что MyList будет мутировать его. Это рекомендуется по документации.

Вы можете увидеть обновленную скрипту здесь: https://jsfiddle.net/kxrf5329/

Ответ 2

Вы должны вызвать повторный рендеринг, вызвав setState() и предоставив новый реквизит, который вы хотите распространять. Если вы действительно хотите принудительно выполнить обновление, вы также можете вызвать forceUpdate().

Если вы посмотрите на примеры на странице, вы увидите, что setState - это метод, используемый для обновления и запуска повторного использования, рендеринг. Документация также четко указывает (ahaha!).

В вашем случае я бы назвал forceUpdate.

РЕДАКТИРОВАТЬ: Как отметила Иордания в комментарии, было бы лучше хранить предметы как часть вашего государства. Таким образом вам не нужно было бы вызывать forceUpdate, но вы действительно обновили бы состояние своего компонента, поэтому регулярный setState с обновленными значениями будет работать лучше.