Статус ReactJS против опоры

Это может занять эту грань между ответственным и упрямым, но я собираюсь вернуться к тому, как структурировать компонент ReactJS по мере роста сложности и может использовать какое-то направление.

Начиная с AngularJS, я хочу передать свою модель в компонент как свойство и напрямую изменить модель. Или я должен разбить модель на различные свойства state и собрать ее обратно вместе при отправке назад вверх по течению? Что такое способ ReactJS?

Возьмем, например, редактор блога. Попытка изменить модель напрямую выглядит следующим образом:

var PostEditor = React.createClass({
  updateText: function(e) {
    var text = e.target.value;
    this.props.post.text = text;
    this.forceUpdate();
  },
  render: function() {
    return (
      <input value={this.props.post.text} onChange={this.updateText}/>
      <button onClick={this.props.post.save}/>Save</button>
    );
  }
});

Что кажется неправильным.

Это скорее способ React, чтобы сделать наше свойство модели text state, и скомпилировать его обратно в модель перед сохранением, например:

var PostEditor = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },
  componentWillMount: function() {
    this.setState({
      text: this.props.post.text
    });
  },
  updateText: function(e) {
    this.setState({
      text: e.target.value
    });
  },
  savePost: function() {
    this.props.post.text = this.state.text;
    this.props.post.save();
  },
  render: function() {
    return (
      <input value={this.state.text} onChange={this.updateText}/>
      <button onClick={this.savePost}/>Save</button>
    );
  }
});

Это не требует вызова this.forceUpdate(), но по мере роста модели (почта может иметь автора, тему, теги, комментарии, оценки и т.д.), компонент начинает становиться действительно сложным.

Является первым методом с ReactLink способом?

Ответ 1

Ваш второй подход больше похож на это. React заботится не столько о моделях, сколько о ценностях и о том, как они проходят через ваше приложение. В идеале ваша модель поста будет храниться в одном компоненте в корне. Затем вы создаете дочерние компоненты, каждый из которых использует части модели.

Вы можете передавать обратные вызовы дочерним элементам, которым необходимо изменить ваши данные, и вызывать их из дочернего компонента.

Непосредственное изменение this.props или this.state не является хорошей идеей, потому что React не сможет принять изменения. Это потому, что React делает небольшое сравнение вашего поста, чтобы определить, изменился ли он.

Я сделал это jsfiddle, чтобы показать, как данные могут передаваться из внешнего во внутренний компонент.

Метод handleClick показывает 3 способа (im) правильного обновления состояния:

var Outer = React.createClass({

  getInitialState: function() {
    return {data: {value: 'at first, it works'}};
  },

  handleClick: function () {

    // 1. This does not work, render is not triggered.
    // Never set state directly because the updated values
    // can still be read, which can lead to unexpected behavior.

    this.state.data.value = 'but React will never know!';

    // 2. This works, because we use setState

    var newData = {value: 'it works 2'};
    this.setState({data: newData});

    // 3. Alternatively you can use React immutability helpers
    // to update more complex models.
    // Read more: http://facebook.github.io/react/docs/update.html

    var newState = React.addons.update(this.state, {
      data: {value: {$set: 'it works'}}
    });
    this.setState(newState);
 },

  render: function() {
      return <Inner data={this.state.data} handleClick={this.handleClick} />;
  }
});

Ответ 2

Обновление 2016: Реакция изменилась, и объяснение "реквизит против государства" стало очень простым. Если компоненту необходимо изменить данные - поставьте его в состояние, иначе в реквизитах. Поскольку реквизиты доступны только для чтения.

Какая точная разница между реквизитами и состоянием?

Вы можете найти хорошее объяснение здесь (полная версия)

Changing props and state

Ответ 3

Из React doc

реквизиты неизменяемы: они передаются от родителя и "принадлежат" родителям. Чтобы реализовать взаимодействия, мы вводим переменное состояние в компонент. this.state является частным компонентом и может быть изменен путем вызова this.setState(). Когда состояние обновляется, компонент повторно отображает себя.

От TrySpace: при обновлении реквизита (или состояния) (через setProps/setState или parent) компонент повторно отображает.

Ответ 4

Чтение из Thinking in React:

Позвольте пройти через каждый и выяснить, какой из них является государством. Просто задать три вопроса о каждом фрагменте данных:

  1. Это передается от родителей через реквизит? Если так, то, вероятно, нет государство.
  2. Это меняется со временем? Если нет, то, вероятно, это не состояние.

  3. Можете ли вы рассчитать его на основе любого другого состояния или реквизита в вашем     составная часть? Если так, то это не государство.

Ответ 5

Я не уверен, отвечаю ли я на ваш вопрос, но я обнаружил, что, особенно в большом/растущем приложении, шаблон Контейнер/Компонент работает невероятно хорошо.

По сути, у вас есть два компонента React:

  • "чистый" компонент дисплея, который имеет дело со стилем и взаимодействием DOM;
  • компонент контейнера, который имеет дело с доступом/сохранением внешних данных, управлением состоянием и отображением компонента отображения.

Пример

N.B. Этот пример, вероятно, слишком прост, чтобы проиллюстрировать преимущества этого шаблона, поскольку он довольно многословен для такого простого случая.

/**
 * Container Component
 *
 *  - Manages component state
 *  - Does plumbing of data fetching/saving
 */

var PostEditorContainer = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },

  componentWillMount: function() {
    this.setState({
      text: getPostText()
    });
  },

  updateText: function(text) {
    this.setState({
      text: text
    });
  },

  savePost: function() {
    savePostText(this.state.text);
  },

  render: function() {
    return (
      <PostEditor
        text={this.state.text}
        onChange={this.updateText.bind(this)}
        onSave={this.savePost.bind(this)}
      />
    );
  }
});


/**
 * Pure Display Component
 *
 *  - Calculates styling based on passed properties
 *  - Often just a render method
 *  - Uses methods passed in from container to announce changes
 */

var PostEditor = React.createClass({
  render: function() {
    return (
      <div>
        <input type="text" value={this.props.text} onChange={this.props.onChange} />
        <button type="button" onClick={this.props.onSave} />
      </div>
    );
  }
});

Преимущества

Сохраняя логику отображения и управление данными/состоянием отдельно, вы получаете повторно используемый компонент отображения, который:

  • может быть легко повторен с различными наборами реквизита, используя что-то вроде реагировать-компонент-площадка
  • может быть упакован в другой контейнер для различного поведения (или объединен с другими компонентами для создания больших частей вашего приложения

У вас также есть контейнерный компонент, который имеет дело со всеми внешними коммуникациями. Это должно упростить настройку доступа к вашим данным, если впоследствии вы внесете какие-либо серьезные изменения *.

Этот шаблон также упрощает написание и реализацию модульных тестов.

Пройдя несколько раз итерацию большого приложения React, я обнаружил, что этот шаблон сохраняет все относительно безболезненно, особенно если у вас есть большие компоненты с вычисляемыми стилями или сложные взаимодействия DOM.

* Прочтите образец потока и взгляните на Marty.js, который во многом вдохновил этот ответ (и я много использовал в последнее время) Reduxresponse-redux), которые очень хорошо реализуют этот шаблон.

Примечание для тех, кто читает это в 2018 году или позже:

Со времени написания этого ответа React претерпел немало изменений, особенно с появлением Hooks. Однако базовая логика управления состояниями из этого примера остается той же, и, что более важно, преимущества, которые вы получаете от разделения вашей логики состояния и представления, по-прежнему применяются одинаково.

Ответ 6

Я думаю, что вы используете анти-шаблон, который Facebook уже объяснил по этой ссылке

Вот что вы нашли:

React.createClass({
  getInitialState: function() {
    return { value: { foo: 'bar' } };
  },

  onClick: function() {
    var value = this.state.value;
    value.foo += 'bar'; // ANTI-PATTERN!
    this.setState({ value: value });
  },

  render: function() {
    return (
      <div>
        <InnerComponent value={this.state.value} />
        <a onClick={this.onClick}>Click me</a>
      </div>
    );
  }
});

При первом рендеринге внутренний компонент будет иметь значение {foo: 'bar'} в качестве значения prop. Если пользователь щелкнет на якоре, состояние родительского компонента будет обновлено до {value: {foo: 'barbar'}}, запустив процесс повторной визуализации внутреннего компонента, который получит {foo: 'barbar'} как новое значение для опоры.

Проблема состоит в том, что, поскольку родительский и внутренний компоненты совместно используют ссылку на один и тот же объект, когда объект мутирует в строке 2 функции onClick, пропеллер, который имел внутренний компонент, изменится. Таким образом, когда начинается процесс повторного рендеринга, и должен вызываться shouldComponentUpdate, this.props.value.foo будет равно nextProps.value.foo, потому что фактически this.props.value ссылается на тот же объект, что и nextProps.value.

Следовательно, поскольку мы пропустим изменение в подпорке и закроем процесс повторного рендеринга, пользовательский интерфейс не будет обновлен с "bar" до "barbar".