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