Как компонент, связанный с сокращением, знает, когда нужно повторно отобразить?

Я, вероятно, упускаю что-то очень очевидное и хотел бы очистить себя.

Здесь мое понимание.
В наивном компоненте реакции мы имеем states & props. Обновление state с помощью setState повторно отображает весь компонент. props в основном только для чтения, и их обновление не имеет смысла.

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

response-redux имеет помощника connect(), который внедряет часть дерева состояний (которая представляет интерес для компонента) и actionCreators как props в компонент, обычно через что-то вроде

const TodoListComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

Но, понимая, что setState необходим для того, чтобы TodoListComponent реагировал на изменение дерева состояний редукса (повторное рендеринг), я не могу найти никакого кода, связанного с state или setState, в TodoList файл компонента. Это звучит примерно так:

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

Может ли кто-нибудь указать мне правильное направление относительно того, что мне не хватает?

P.S. Я следую примеру списка задач, который поставляется в комплекте с избыточным пакетом.

Ответы

Ответ 1

Функция connect генерирует компонент-оболочку, который подписывается на хранилище. Когда отправляется действие, вызывается обратный вызов компонента-оболочки. Затем он запускает вашу функцию mapState и поверхностно сравнивает объект результата этого времени с объектом результата последнего времени (поэтому, если бы вы переписали поле избыточного хранилища с тем же значением, это не вызывать повторную визуализацию). Если результаты отличаются, то он передает результаты вашему "реальному" компоненту "в качестве реквизита.

Дан Абрамов написал большую упрощенную версию connect в (connect.js), которая иллюстрирует основную идею, хотя она не показывает какую-либо работу по оптимизации. У меня также есть ссылки на ряд статей о производительности Redux, в которых обсуждаются некоторые связанные идеи.

обновление

React-Redux v6.0.0 внес некоторые важные внутренние изменения в то, как подключенные компоненты получают свои данные из хранилища.

В связи с этим я написал пост, в котором объясняется, как работает API connect и его внутренние компоненты, и как они менялись со временем:

Идиоматический Redux: история и реализация React-Redux

Ответ 2

Мой ответ немного за пределами левого поля. Это проливает свет на проблему, которая привела меня к этому посту. В моем случае казалось, что приложение не рендерится, хотя и получило новые реквизиты.
У разработчиков React был ответ на этот часто задаваемый вопрос, который звучал так: если (хранилище) было изменено, то 99% времени, когда причина реагирует, не будет повторно отображаться. Еще ничего про остальные 1%. Мутация не была здесь.


TL;DR;

componentWillReceiveProps - это то, как state может синхронизироваться с новыми props.

Пограничный случай: после обновления state приложение снова рендерится!


Оказывается, что если ваше приложение использует только state для отображения своих элементов, props могут обновляться, а state - нет, поэтому повторного рендеринга нет.

У меня было state которое зависело от props полученного из store. Нужных мне данных еще не было в хранилище, поэтому я извлек их из componentDidMount, как и положено. Я получил реквизит обратно, когда мой редуктор обновил хранилище, потому что мой компонент подключен через mapStateToProps. Но страница не визуализировалась, и состояние все еще было полно пустых строк.

Примером этого является, скажем, пользователь загрузил страницу "Редактировать пост" из сохраненного URL. У вас есть доступ к postId из URL, но информация еще не store, поэтому вы выбираете ее. Элементы на вашей странице являются контролируемыми компонентами, поэтому все отображаемые данные находятся в state.

С использованием приставки данные были получены, хранилище обновлено, а компонент connect, но приложение не отразило изменения. При ближайшем рассмотрении были получены props, но приложение не обновилось. state не обновлялось.

Ну, props будет обновляться и распространяться, но state не будет. Вы должны специально указать state для обновления.

Вы не можете сделать это в render(), и componentDidMount уже завершил его циклы.

componentWillReceiveProps - это место, где вы обновляете свойства state которые зависят от измененного значения prop.

Пример использования:

componentWillReceiveProps(nextProps){
  if (this.props.post.category !== nextProps.post.category){
    this.setState({
      title: nextProps.post.title,
      body: nextProps.post.body,
      category: nextProps.post.category,
    })
  }      
}

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

Методы жизненного цикла компонентов ReactJ - глубокое погружение

componentWillReceiveProps вы будете обновлять state чтобы синхронизировать его с обновлениями props.

Как только state обновляется, тогда поля в зависимости от state перерисовываются!

Ответ 3

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

1-store State change-2-call (componentWillRecieveProps (() => {3-компонентное изменение состояния}))

Ответ 4

Этот ответ является кратким изложением статьи Брайана Вона, озаглавленной " Вероятно, вам не нужно производное состояние" (07 июня 2018 г.).

Получение состояния от реквизита является анти-паттерном во всех его формах. В том числе с использованием более старых componentWillReceiveProps и более новых getDerivedStateFromProps.

Вместо получения состояния из реквизита, рассмотрите следующие решения.

Две лучшие рекомендации

Рекомендация 1. Полностью контролируемый компонент
function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}
Рекомендация 2. Полностью неуправляемый компонент с ключом
// parent class
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

// child instance
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Два варианта, если по какой-либо причине рекомендации не подходят для вашей ситуации.

Альтернатива 1: сбросить неконтролируемый компонент с помощью идентификатора проп
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that just the email.
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}
Альтернатива 2: сбросить неуправляемый компонент методом экземпляра
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}