Redux - 了解高级mapStateToProps返回函数

时间:2018-04-24 16:40:08

标签: javascript reactjs redux react-redux

我试图在返回函数时理解mapStateToProps的机制。

所以我找不到很多文档,除了Redux文档的一个简短的摘录,通过返回一个函数预先说明案例,每个实例将获得自己的memoized mapStateToProps,另一个用户说这是一个阻止mapStateToProps的优化要求任何父道具改变。

所以这对列表项来说似乎很棒,我不想为任何不影响项目的更改重新呈现大项目列表。

所以令我困惑的部分是,不会为任何父道具更改调用mapStateToProps,这是否意味着为了重新呈现单个列表'Item',它需要是一个智能连接组件获取它关心并重新渲染的更改?或者这是否意味着它永远不会为这个特定的Item实例重新渲染?

更新

想要澄清一下,我正在具体谈论mapStateProps的工厂功能版本。

以下是我所讨论的功能,取自React-Redux文档:

注意:在需要更多控制渲染性能的高级场景中,mapStateToProps()也可以返回一个函数。在这种情况下,该函数将用作特定组件实例的mapStateToProps()。这允许您进行每个实例的memoization。您可以参考#279及其添加的测试以获取更多详细信息。大多数应用都不需要这个。

从本文中摘录的段落:

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

如果redux接收到一个返回函数的实现,它会执行一个闭包来包装组件自己的props,因此每次组件更改它从父组件接收的props时都会绕过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 :(得分:5)

如果状态发生变化,将始终调用每个mapStateToProps函数。 redux中没有包含阻止mapStateToProps被调用的机制。

  

From the docs for connect()

     

mapStateToProps(state, [ownProps]): stateProps] (Function):如果指定了此参数,则新组件将订阅Redux商店更新。这意味着任何时候更新商店,都会调用mapStateToProps

您想要阻止的(通常使用选择器)是mapStateToProps内发生的昂贵计算将在每次状态更新时重复,即使它们产生相同的结果。

  

或者这意味着它永远不会为这个特定的物品重新渲染   实例

如果连接组件收到的任何道具发生变化,它将照常重新渲染。关键是要阻止mapStateToProps进行昂贵的计算。 mapStateToProps愚蠢。它进行计算并将它们作为道具传递给连接的组件。然后该组件检查道具是否与之前的道具不同,并决定根据该道具重新渲染。

考虑使用选择器mapStateToProps的<{1}}函数:

getVisibleTodos

选择器会记住调用的结果,只要输入参数不变,它就会在后续调用中返回该结果。此示例中的选择器仅从redux状态获取其输入。只要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'}的{​​{1}}没有改变,这也会导致选择器重新计算。现在,返回函数的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 } 函数,它具有自己的选择器,以便为每个实例单独存储道具,并且只会重新计算实例的道具是否为为改变而创造。这将解决前一个示例中的问题。

所以tl; dr;:当mapStateToProps内的选择器将连接组件的自己的道具作为参数时,您需要使用TodoList作为工厂来允许每个实例的记忆。

您可以使用redux文档中的Comuting Derived Data下的示例找到更详细的说明。

答案 1 :(得分:1)

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 }并决定是否从列表中显示所选项目的详细信息。