在观看Dan Abramov的new egghead course之后,我对提到的选择器有疑问。
选择器的目的是从组件中隐藏状态树的详细信息,以便在树更改时更容易管理代码。
如果我理解正确,那意味着,在mapStateToProps
内调用的选择器应该只是位于顶级reducer中的选择器。因为传递给state
的{{1}}是整个应用程序状态树。如果这是真的,随着应用程序的增长,我可以想象管理顶级选择器会变得非常困难。
我想念这个概念吗?或者这是一个有效的问题?
修改:试图让我的问题更加清晰。
说我的整个州开始
mapStateToProps
我有
{ byIds, listByFilter }
在我的顶级缩减器export const getIsFetching = (state, filter) =>
fromList.getIsFetching(state.listByFilter[filter]);
中,组件只使用reducers/index.js
将整个状态传递给is,这是完全正常的,因为它是顶层。
然而,稍后,我决定我的整个应用程序将包含一个待办事项应用程序和一个计数器应用程序。因此,将当前的顶级缩减器放入getIsFetching
是有意义的,并创建一个新的顶级缩减器reducers/todo.js
,如下所示:
reducers/index.js
在我的状态就像
combineReducers({
todo: todoReducer,
counter: counterReducer
})
组件无法再使用{
todo: {
byIds,
listByFilter
},
counter: {
// counter stuff
}
}
中的getIsFetching
,因为reducers/todo.js
中的状态现在实际上正在处理getIsFetching
。所以我必须在顶级缩减器state.todo
导出另一个选择器,如下所示:
reducers/index.js
仅在此时,组件才能使用export const getIsFetching = (state, filter) =>
fromTodo.getIsFetching(state.todo);
而不必担心状态形状。
然而,这引起了我的担忧,即组件直接使用的所有选择器都必须存在于顶级缩减器中。
更新2 :基本上我们将选择器从最深层一直导出到顶层缩减器,而中间缩减器中的所有导出器都没有使用它们,但它们在那里因为减速器知道该级别的状态形状。
这非常类似于将getIsFetching
从父级一直传递给子级,而中级组件不使用props
。我们通过props
或context
来避免这种情况。
为可怜的英语道歉。
答案 0 :(得分:1)
因此,虽然mapStateToProps
确实占用了整个状态树,但是您可以从该状态返回您想要的内容以呈现您的组件。
例如,我们可以看到他调用getVisibleTodos
并传入state
(和路由器中的params
),然后返回已过滤的todos
列表:< / p>
<强>组件/ VisibleTodoList.js 强>
const mapStateToProps = (state, { params }) => ({
todos: getVisibleTodos(state, params.filter || 'all'),
});
通过跟随调用,我们可以看到商店正在使用combineReducers
(尽管只有一个减速器),因此,这需要他将状态树的适用部分传递给{{1减速器,当然是todos
。
<强>减速器/ index.js 强>
state.todos
虽然import { combineReducers } from 'redux';
import todos, * as fromTodos from './todos';
const todoApp = combineReducers({
todos,
});
export default todoApp;
export const getVisibleTodos = (state, filter) =>
fromTodos.getVisibleTodos(state.todos, filter);
会返回getVisibleTodos
的列表,但是它是顶级todos
的直接子集(并且同样如此命名),我相信这只是为了简单起见示威:
我们可以轻松地编写另一个组件,其中state.todos
类似于:
<强>组件/ NotTopLevel.js 强>
mapStateToProps
在这种情况下,const mapStateToProps = (state, { params }) => ({
todoText: getSingleTodoText(state, params.todoId),
});
仍然接受完整的getSingleTodoText
(以及来自state
的{{1}}),但它只会返回id
的文字,甚至不是完整的对象,也不是顶级params
的列表。因此,在呈现时,您需要决定从商店中取出什么以及填充到组件中。
答案 1 :(得分:1)
我也遇到过这个问题(并且很难解释它......)。我的分区化解决方案遵循redux-forms
处理它的方式。
基本上问题归结为一个问题 - 减速器绑定在哪里?在redux-forms
中,他们假设您在全局状态下将其设置为form
(尽管您可以更改此值)。
因为你已经假定了这一点,你现在可以编写模块的选择器来接受globalState
并返回一个选择器,如下所示:(globalState) => globalState.form.someInnerAttribute
或任何你想要的。
为了使其更具可扩展性,您可以创建一个内部变量来跟踪状态在全局状态树中的绑定位置,还可以创建一个类似getStateFromGlobalState = (globalState) => globalState[boundLocation]
的内部函数,并使用它来获取内部状态树。然后,如果您决定将状态绑定到全局状态树中的不同位置,则可以以编程方式更改此变量。
这样,当您导出模块的选择器并在mapStateToProps中使用它们时,它们可以接受全局状态。如果对reducer的绑定位置进行任何更改,则只需更改一个内部函数。
IMO,这比重写顶级的每个嵌套选择器要好。这很难扩展/维护,需要大量的样板代码。这使减速器/选择器模块保持自身。它唯一需要知道的是减速器必然会被绑定的地方。
顺便说一句 - 你可以为一些深度嵌套的状态执行此操作,在这些状态中,您不必从globalState
引用此状态,而是在状态树上引用某个上级节点。虽然如果你有一个超级嵌套状态,从较高状态的POV中编写选择器可能更有意义。