ReactJS: Сколько раз вызывается конструктор сопоставленного компонента? - PullRequest
0 голосов
/ 30 октября 2018

У меня есть компонент React, который отображается несколько раз с использованием Array.prototype.map () внутри другого компонента.

Сколько раз должен вызываться конструктор первого компонента? Я ожидал столько же раз, сколько длина сопоставленного массива, но кажется, что он вызывается только один раз.

В моем конкретном коде он вызывается непосредственно перед последним рендером ().

Пример кода:

class ComponentB extends Component {
  constructor(props) {
    super(props) // added upon first reply
    this.handleObjectAdd = this.handleObject.bind(this);
    this.state.objects = [];
  }

  handleObjectAdd() {
    this.state.objects.unshift({prop1: this.state.objects.length + 1});
  }

  render() {
      return (
        <div>
          <button onClick={this.handleObjectAdd}>ADD</button>
          { this.state.objects.map((object, index) =>
              <ComponentA key={index} details={object}/>
            )
          }
        </div>
      )
    })
  }
}

class ComponentA extends Component {
  constructor(props) {
    super(props) // added upon first reply
    console.log('ComponentA constructor called');
  }
  render() {
    console.log('ComponentA render() called');
    return (
      <input type="text" value={this.props.details.prop1}></input>
    )
  }
}

Для массива из 5 элементов (все экземпляры ComponentA) я получаю следующие строки:

ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA render() called
ComponentA constructor called
ComponentA render() called

Кроме того, строка журнала для конструктора всегда появляется перед последней строкой журнала конструктора, независимо от количества элементов массива.

Почему вывод журнала такой, как указано выше? Буду признателен за любые идеи.

Это было помечено как дубликат React Rerender динамических компонентов (Solr) , но это не так.

1 Ответ

0 голосов
/ 30 октября 2018

Я понимаю ваш вопрос как "почему, когда я помещаю новый элемент в самом начале массива, кажется, что новый компонент React создан для самого последнего элемента?"

Причина в том, что key используется для index.

Исходя из его логики (отметьте Lists and Keys раздел в документации), поскольку только последний элемент (с новым индексом oldLength + 1) имеет уникальный key, только он будет создан с нуля. Пока все остальные просто перерисованы и обновлены. Другими словами, ваш код обновляет элементы N - 1 вместо того, чтобы просто создавать 1 новый и оставлять все остальные без изменений.

Чтобы справиться с этим, вы не должны полагаться на index в key, но должны использовать другое предсказуемое, стабильное и незанятое значение. В вашем случае это prop1. Тогда конструктор будет вызван для самого первого элемента.

Вот обновленная версия

class ComponentB extends Component {
  constructor(props) {
    super(props) // added upon first reply
    this.state = {
      objects: []
    };
  }

  handleObjectAdd = () => {
    this.setState(oldState => ({
      objects: [
          {prop1: this.state.objects.length + 1}, 
          ...oldState.objects
      ]
    }))
  }


  render() {
    return (
      <div>
        <button onClick={this.handleObjectAdd}>ADD</button>
        {this.state.objects.map(obj =>
          <ComponentA key={obj.prop1} details={obj} />
        )}
      </div>
    )
  }
}

class ComponentA extends Component {
  constructor(props) {
    super(props) // added upon first reply
    console.log('ComponentA constructor called');
  }
  render() {
    console.log('ComponentA render() called');
    return (
      <input type="text" value={this.props.details.prop1}></input>
    )
  }
}

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

[UPD] извлек часть фрагмента из комментариев ниже

вместо того, чтобы просто создать 1 новое и оставить все остальное без изменений.

React не пытается минимизировать количество операций. При сравнении [1,2,3] и [0, 1, 2, 3] есть 2 возможных способа: либо «вставить 0 в начале, сдвигая все остальное», либо «уменьшить все элементы на 1 и дополнительно вставить 3 в конец'. Если вы предоставляете хорошую недвижимость в качестве ключа, React подбирает 1-е решение. Но имея key = {index}, вы буквально говорите: «Реагируйте, используйте 2-й подход, я знаю, что делаю». React не анализирует код, который вы запускаете перед вызовом .setState, он просто полагается на значение ключа.

...