Redux - Понимание расширенного mapStateToProps, возвращающего функцию - PullRequest
0 голосов
/ 24 апреля 2018

Я пытаюсь понять механизм mapStateToProps, когда он возвращает функцию.

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

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

То, что меня смущает, так это то, что mapStateToProps не будет вызываться для каких-либо изменений родительских реквизитов, означает ли это, что для повторного рендеринга отдельного списка 'Item' он должен быть интеллектуальным компонентом для него подобрать изменения, о которых он заботится, и сделать заново? Или это означает, что он никогда не будет повторно визуализироваться для этого конкретного экземпляра Item?

Обновление:

Хотел уточнить, что я говорю именно о заводской версии функции mapStateProps.

Вот функция, о которой я говорю, взята из документации React-Redux:

Примечание: в сложных сценариях, где вам требуется больше контроля над производительностью рендеринга, mapStateToProps () также может возвращать функцию. В этом случае эта функция будет использоваться как mapStateToProps () для конкретного экземпляра компонента. Это позволяет вам делать заметки для каждого экземпляра. Вы можете обратиться к # 279 и тестам, которые он добавляет, для более подробной информации. Большинству приложений это никогда не нужно.

И абзац взят из этой статьи:

https://medium.com/@cvetanov/redux-mapstatetoprops-optimization-5880078a8a7a

Если redux получает реализацию, которая возвращает функцию, он выполняет замыкание для обертывания собственных реквизитов компонента и поэтому обходит вызов mapStateToProps каждый раз, когда компонент меняет свои реквизиты, полученные от родительского компонента. Это создает так называемый purePropsSelector. Как это делается, можно увидеть здесь.

Обновление 2:

Правильно. Я исследую статью, в которой упоминается пропуск, похоже, что когда вы закрываете собственный реквизит в замыкании и возвращаете функцию, которая использует только состояние. Таким образом, он предотвращает вызов mapStateToProps при изменении родительских реквизитов для каждого «подключенного» дочернего элемента.

Это взято из той средней статьи, которую я прочитал выше:

function mapStateToPropsFactory(initialState, ownProps) {
  // a closure for ownProps is created
  // this factory is not invoked everytime the component
  // changes it's props
  return function mapStateToProps(state) {
    return {
      blogs:
        state.blogs.filter(blog => blog.author === ownProps.user)
    };
  };
}
export default connect(mapStateToPropsFactory)(MyBlogs);

Ответы [ 2 ]

0 голосов
/ 28 апреля 2018

Второй аргумент ownProps - это реквизиты, которые были переданы компоненту, который вы упаковываете. Вы упомянули пункты списка, поэтому давайте назовем это (ListItem).

Таким образом, компоненту ListItem, когда он отображается на экране, передается некоторая библиотека списков, например,

const { id, title } = this.props.library;

Объект ownProps точно равен this.props внутри компонента.

Таким образом, любой элемент, который вы передаете компоненту, будет отображаться в mapStateToProps как ownProps. Получая доступ к ownProps внутри этого mapStateToProps:

const mapStateToProps = (state, ownProps) => {
  return { selectedLibraryId: state.selectedLibraryId };
};

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

const mapStateToProps = (state, ownProps) => {
  const selected = state.selectedLibraryId === ownProps.library.id;
  return { selected };
};

Компоненту больше не нужно беспокоиться о значении selectedLibraryId, он может просто посмотреть на { selected } и решить, показывать или нет детали выбранного элемента из списка.

0 голосов
/ 24 апреля 2018

Каждая функция mapStateToProps всегда вызывается при изменении состояния.В редуксе нет механизма, препятствующего вызову mapStateToProps.

Из документов для connect():

mapStateToProps(state, [ownProps]): stateProps] (Function): если указан этот аргумент, новый компонент будет подписываться на обновления хранилища Redux.Это означает, что при каждом обновлении хранилища mapStateToProps будет называться .

Что вы хотите предотвратить (обычно с помощью селекторов), так это то, что дорогостоящие вычисления, происходящие внутри mapStateToProps, будут повторяться при каждом обновлении состояния, даже если они приводят к одному и тому же результату.

Или это означает, что он никогда не будет повторно визуализироваться для этого конкретного экземпляра Item?

Он будет повторно визуализироваться как обычно, если какие-либо реквизиты, полученные подключенным компонентом, изменятся.Дело в том, чтобы mapStateToProps не делал дорогих вычислений.mapStateToProps тупой.Он выполняет свои вычисления и передает их подключенному компоненту в качестве реквизита.Затем компонент проверяет, отличаются ли реквизиты от предыдущих, и решает выполнить повторную визуализацию на основании этого.

Рассмотрим эту функцию mapStateToProps, которая использует селектор getVisibleTodos:

const mapStateToProps = state => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

Селекторы запоминают результат вызова и просто возвращают этот результат при последующих вызовах, пока входные параметры не изменятся.Селектор в этом примере получает свои входные данные только из состояния избыточности.Пока state.todos и state.visibilityFilter не меняются, он может использовать запомненный результат последнего вызова и не нуждается в пересчете чего-либо.

Теперь рассмотрим еще один пример:

const TodoList = ({id, todos}) => (
  <ul id={id}>
    {todos.map(/* ... */)}
  </ul>
);

const mapStateToProps = (state, props) => {
  return {
    todos: getVisibleTodos(state, props)
  }
}

export default connect(mapStateToProps)(TodoList);

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

<TodoList id="list1" />
<TodoList id="list2" />

, это приведет к тому, что mapStateToProps будет вызываться дважды при обновлениях состояния, по одному разу для каждого TodoList экземпляра,И оба раза он получит разные реквизиты.Один раз с {id: 'list1'} и второй раз с {id: 'list2'}.Но оба компонента имеют один и тот же селектор.Это приведет к повторному вычислению селектором, даже если todos для каждого отдельного TodoList не изменилось.Теперь в игру вступает функция mapStateToProps, которая возвращает функцию сама по себе:

const makeMapStateToProps = () => {
  const getVisibleTodos = makeGetVisibleTodos() // this creates a new selector function
  const mapStateToProps = (state, props) => {
    return {
      todos: getVisibleTodos(state, props)
    }
  }
  return mapStateToProps
}

Это создает отдельную функцию mapStateToProps для каждого экземпляра TodoList, которая имеет свой собственный селектор, так что она запоминаетреквизиты для каждого экземпляра индивидуально и будут пересчитываться только в том случае, если реквизиты для экземпляра были созданы для измененияЭто решит проблему из предыдущего примера.

So tl; dr ;: Когда селектор внутри mapStateToProps принимает собственный реквизит подключенного компонента в качестве параметра, вам необходимо использовать mapStateToProps в качестве фабрики, чтобы учесть запоминание для каждого экземпляра.

Более подробное объяснение можно найти с примером в разделе Вычисление производных данных в документах Redx.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...