React + Redux,我的UI相关的计算应该去哪里,容器组件,还是reducer?
React + Redux, where should my UI-related calculations go, container components, or reducers?
我正在从事一个中型到大型 react/redux 项目,我正在努力寻找 react/redux 的最佳实践。
例如,如果我有一个 饼图 组件,它将采用一个简单的数组作为 prop:
[{
label: 'a',
value: 10
}, {
label: 'b',
value: 6
}, ...]
但是我从API服务器获取的源数据可能是这样的:
{
a: {
value1: 15,
value2: 3,
value3: 7,
...
},
b: { ... }
}
现在我必须做一些计算来得到结果数据(比如 value1 -value2 + value3...也许一些过滤器...),问题是,我应该把这个计算放在 reducer(我觉得它更像是一个 getter,它将通过 connect
作为 prop 传递给组件,例如 getCartProducts
in this example),或 容器组件(在这种情况下可能是 仪表板)?
我认为这两个选项都有意义:
在 reducer 中:我们可以保持我们的组件干净,并将所有逻辑放在同一个地方。
在容器中:因为是UI相关的,如果计算出来的数据是不可复用的(只针对那个饼图),我们可以把这种计算放在相关的组件中.
如果我选择reducers,那么另一个问题来了。我应该将计算后的数据保存在商店中吗?或者只是保存源数据,并在每次渲染组件时计算它? The old React doc 说你不应该在状态中保存计算数据,不确定是否适用于 redux。
通常:
store 应该包括最小可能的状态。
派生的数据是通过reselect在mapStateToProps中计算出来的,
记住结果。将立即调用计算;
但是:
如果派生数据的形状与应用程序中使用的形状不同(或者您需要一些计算),则
你可以在获取后立即计算。 (例如它可能正在规范化数据)
我认为您应该持久化集合而不是数组,因为通过键获取数据要简单得多。
如果您不打算编辑特定项目的值,您可以在 reducer 中预先计算它们。
例如通过选择器计算:
使用了选择器组合,以避免在更新过滤器时重新计算:
import map from 'lodash/map';
import { createSelector } from 'reselect';
const getFilterKeys=(state)=>state.filter; //['a','b','c']
//(btw it's just example... filter may be in props);
const getItems=(state)=>state.entities.items;
const calculateValues = (item) => item.value1 - item.value2 + item.value3;
export const getItemsForChart = createSelector(
[getItems],
(items)=> map(items,(item,key)=>({label:key, value:calculateValues(item)}))
);
export const getItemsForChartWithFilter = createSelector(
[getItemsForChart, getFilterKeys],
(items, filterKeys)=> items.filter(item=>filterKeys.some(key=>key==item.label))
);
.
const mapStateToProps=(state)=>{
return {
chartData:getItemsForChartWithFilter(state)
};
};
顺便说一句,集合中的顺序无法保证,因此除了集合之外,您可能还应该从服务器接收键数组。然后代码会有点不同...
我正在从事一个中型到大型 react/redux 项目,我正在努力寻找 react/redux 的最佳实践。
例如,如果我有一个 饼图 组件,它将采用一个简单的数组作为 prop:
[{
label: 'a',
value: 10
}, {
label: 'b',
value: 6
}, ...]
但是我从API服务器获取的源数据可能是这样的:
{
a: {
value1: 15,
value2: 3,
value3: 7,
...
},
b: { ... }
}
现在我必须做一些计算来得到结果数据(比如 value1 -value2 + value3...也许一些过滤器...),问题是,我应该把这个计算放在 reducer(我觉得它更像是一个 getter,它将通过 connect
作为 prop 传递给组件,例如 getCartProducts
in this example),或 容器组件(在这种情况下可能是 仪表板)?
我认为这两个选项都有意义:
在 reducer 中:我们可以保持我们的组件干净,并将所有逻辑放在同一个地方。
在容器中:因为是UI相关的,如果计算出来的数据是不可复用的(只针对那个饼图),我们可以把这种计算放在相关的组件中.
如果我选择reducers,那么另一个问题来了。我应该将计算后的数据保存在商店中吗?或者只是保存源数据,并在每次渲染组件时计算它? The old React doc 说你不应该在状态中保存计算数据,不确定是否适用于 redux。
通常:
store 应该包括最小可能的状态。
派生的数据是通过reselect在mapStateToProps中计算出来的, 记住结果。将立即调用计算;
但是: 如果派生数据的形状与应用程序中使用的形状不同(或者您需要一些计算),则 你可以在获取后立即计算。 (例如它可能正在规范化数据)
我认为您应该持久化集合而不是数组,因为通过键获取数据要简单得多。 如果您不打算编辑特定项目的值,您可以在 reducer 中预先计算它们。
例如通过选择器计算:
使用了选择器组合,以避免在更新过滤器时重新计算:
import map from 'lodash/map';
import { createSelector } from 'reselect';
const getFilterKeys=(state)=>state.filter; //['a','b','c']
//(btw it's just example... filter may be in props);
const getItems=(state)=>state.entities.items;
const calculateValues = (item) => item.value1 - item.value2 + item.value3;
export const getItemsForChart = createSelector(
[getItems],
(items)=> map(items,(item,key)=>({label:key, value:calculateValues(item)}))
);
export const getItemsForChartWithFilter = createSelector(
[getItemsForChart, getFilterKeys],
(items, filterKeys)=> items.filter(item=>filterKeys.some(key=>key==item.label))
);
.
const mapStateToProps=(state)=>{
return {
chartData:getItemsForChartWithFilter(state)
};
};
顺便说一句,集合中的顺序无法保证,因此除了集合之外,您可能还应该从服务器接收键数组。然后代码会有点不同...