Каждая функция 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.