gpt4 book ai didi

javascript - Redux:将 Selector 与 Reducers 共存

转载 作者:行者123 更新时间:2023-12-03 08:22:46 27 4
gpt4 key购买 nike

在这个 Redux: Colocating Selectors with Reducers蛋头教程,Dan Abramov建议使用接受完整状态树而不是状态切片的选择器来封装远离组件的状态知识。他认为这使得更改状态结构变得更容易,因为组件不知道它,我完全同意这一点。

然而,他建议的方法是,对于对应于特定状态切片的每个选择器,我们在根 reducer 旁边再次定义它,以便它可以接受完整状态。当然,这种实现开销会破坏他试图实现的目标……简化 future 更改状态结构的过程。

在一个有很多 reducer 的大型应用程序中,每个都有很多选择器,如果我们在根 reducer 文件中定义所有选择器,我们是否会不可避免地遇到命名冲突?直接从其相关的 reducer 导入选择器并传入全局状态而不是相应的状态切片有什么问题?例如

const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, todo(undefined, action)];
case 'TOGGLE_TODO':
return state.map(t => todo(t, action));
default:
return state;
}
};

export default todos;

export const getVisibleTodos = (globalState, filter) => {
switch (filter) {
case 'all':
return globalState.todos;
case 'completed':
return globalState.todos.filter(t => t.completed);
case 'active':
return globalState.todos.filter(t => !t.completed);
default:
throw new Error(`Unknown filter: ${filter}.`);
}
};

这样做有什么缺点吗?

最佳答案

我自己犯了这个错误(不是使用 Redux,而是使用类似的内部 Flux 框架),问题在于您建议的方法将选择器耦合到整个状态树中相关 reducer 状态的位置。这在某些情况下会导致问题:

  • 您希望在状态树中的多个位置拥有 reducer(例如,因为相关组件出现在屏幕的多个部分,或者被应用程序的多个独立屏幕使用)。
  • 你想在另一个应用程序中重用reducer,而这个应用程序的状态结构与你原来的应用程序不同。

  • 它还向每个模块的选择器添加了对根 reducer 的隐式依赖(因为它们必须知道它们所在的键,这实际上是根 reducer 的责任)。

    如果选择器需要来自多个不同 reducer 的状态,则问题可能会被放大。理想情况下,模块应该只导出一个将状态切片转换为所需值的纯函数,并由应用程序的根模块文件来连接它。

    一个好技巧是拥有一个只导出选择器的文件,所有选择器都采用状态切片。这样它们就可以批量处理:
    // in file rootselectors.js
    import * as todoSelectors from 'todos/selectors';
    //...
    // something like this:
    export const todo = shiftSelectors(state => state.todos, todoSelectors);

    (shiftSelectors 有一个简单的实现——我怀疑重新选择库已经有一个合适的功能)。

    这也为您提供了名称间距 - 待办事项选择器都在“待办事项”导出下可用。现在,如果您有两个待办事项列表,您可以轻松导出 todo1 和 todo2,甚至可以通过导出一个内存函数来为特定索引或 id 创建它们来提供对动态列表的访问。 (例如,如果您可以一次显示任意一组待办事项列表)。例如。
    export const todo = memoize(id => shiftSelectors(state => state.todos[id], todoSelectors)); 
    // but be careful if there are lot of ids!

    有时选择器需要来自应用程序多个部分的状态。同样,避免在根部以外的地方接线。在您的模块中,您将拥有:
    export function selectSomeState(todos, user) {...}

    然后你的根选择器文件可以导入它,并将连接“待办事项”和“用户”的版本重新导出到状态树的适当部分。

    因此,对于一个小型的一次性应用程序,它可能不是很有用,只是添加了样板文件(特别是在 JavaScript 中,它不是最简洁的函数式语言)。对于使用许多共享组件的大型应用程序套件,它将实现大量重用,并保持职责清晰。它还使模块级选择器更简单,因为它们不必先降到适当的级别。此外,如果您添加 FlowType 或 TypeScript,您可以避免所有子模块必须依赖于您的根状态类型的真正糟糕问题(基本上,我提到的隐式依赖变得显式)。

    关于javascript - Redux:将 Selector 与 Reducers 共存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41274439/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com