ReactJS состояние против проп

107

Это может занять эту грань между ответственным и упрямым, но я собираюсь вернуться к тому, как структурировать компонент 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 способом?

Теги:

6 ответов

56
Лучший ответ

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

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

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

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

http://jsfiddle.net/jxg/M3CLB/

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

var Outer = React.createClass({

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

  handleClick: function () {

    // 1. This doesn't 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} />;
  }
});
  • 0
    Но что нам делать, если у нас есть непрозрачная модель с собственными функциями манипулирования состоянием? Например, предположим, что вместо text поля у нас есть метод setText который выполняет проверку и некоторые другие вещи. Я вижу, как метод (2) работает, если setText является чистым и возвращает новый экземпляр модели. Однако, если setText просто обновляет внутреннее состояние, нам все равно нужно вызвать forceUpdate , верно?
  • 1
    Да, вы можете вызвать forceUpdate , но в этот момент вы «просачиваетесь» из React. Возможно, лучше передать setState() вызов setState() в непрозрачную модель, чтобы избежать необходимости вручную запускать повторные рендеры.
Показать ещё 1 комментарий
83

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

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

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

Изображение 2700

  • 1
    на самом деле setProps () может изменить реквизит внутри компонента и вызвать повторную визуализацию
  • 2
    setProps устарела и не должна использоваться. Замена состоит в том, чтобы заново отрендерить компонент и позволить React справиться с различиями.
Показать ещё 1 комментарий
34

Из React doc

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

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

14

Чтение из Мышление в действии

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

  • Проходит ли он от родителя через реквизит? Если это так, возможно, это не государство.
  • С течением времени меняется? Если нет, возможно, это не состояние.

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

8

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

По существу у вас есть две компоненты 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-component-playground
  • можно обернуть другим контейнером для другого поведения (или объединить с другими компонентами для создания более крупных частей вашего приложения

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

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

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

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

  • 0
    +1000, мы просто использовали этот стиль здесь, и он работал
0

Я думаю, вы используете анти-шаблон, который 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} новое значение для prop.

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

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

  • 0
    Не могли бы вы также Innercomponents код Innercomponents ?
  • 0
    пожалуйста, проверьте jsfiddle.net/chuongxl/L9eeyuxy/20

Ещё вопросы

Сообщество Overcoder
Наверх
Меню