在 redux 中处理计算和格式化数据的最佳方法
Best way to deal with calculating and formatting data in redux
我有一个相当简单的要求,即在我的 Javascript 应用程序中呈现 table 的 start/end-dates:
<table>
<tr>
<th>Start Date</th>
<th>End Date</th>
<th>Number of weeks</th>
</tr>
<tr>
<td>2 Jan 2017</td>
<td>9 Jan 2017</td>
<td>1</td>
</tr>
<!-- etc -->
</table>
我的原始数据是这样的:
[
{
Id: 8672,
StartDate: "2017-01-02",
Weeks: 1
}
// More rows
]
我的问题是执行这些字段的计算和格式设置以保持关注点分离而不会对性能产生不利影响的最佳方法是什么?我考虑了多种方法:
在将数据存储到数据存储之前对其进行转换
我可以使用 reducer 函数,或引发 "data loaded" 操作的代码片段,将原始数据映射到更接近于 UI 组件所需格式和字段的内容.这具有使 UI 组件简单的优点,但它确实将数据和 reducer 与用户界面控件非常紧密地联系在一起。
在UI组件中执行计算和格式化
我可以只格式化和解析用户 UI 组件中的数据。这似乎是个坏主意,因为它将 UI 组件与数据格式紧密联系在一起。
在 table UI
的 mapStateToProps 中执行转换
这似乎是个好方法,但它会强制 react-redux 绑定在每次状态更改时重新渲染整个 table,因为每次渲染都会导致 dates
prop拥有不同的身份
const datesRow = (date) => (
<tr>
<td>{date.StartDate}</td>
<td>{date.EndDate}</td>
<td>{date.NumberOfWeeks}</td>
</tr>
)
const datesTable = ({dates}) => (
<table>
<tr>
<th>Start Date</th>
<th>End Date</th>
<th>Number of weeks</th>
</tr>
{ dates.map(datesRow) }
</table>
);
const mapStateToProps = (state) => ({
dates: state.dates.map(x => transformDates(x))
});
const DatesTableContainer = connect(mapStateToProps)(datesTable);
创建行 UI 组件并在那里进行映射
这似乎保持了 UI 和前一个选项提供的状态之间的松散耦合,但也允许 UI 的增量更新。容器似乎很重。这是个好主意吗?
const datesRow = (date) => (
<tr>
<td>{date.StartDate}</td>
<td>{date.EndDate}</td>
<td>{date.NumberOfWeeks}</td>
</tr>
)
const mapStateToPropsForRow = (state, ownProps) => {
const dateItem = lookupDateById(ownProps.dateId);
return {
StartDate: formatDate(dateItem.StartDate),
EndDate: calculateAndFormatEndDate(dateItem.EndDate),
NumberOfWeeks: dateItem.NumberOfWeeks
}
};
const DatesRowContainer = connect(mapStateToPropsForRow)(datesRow);
const mapStateToPropsForRow = (state, ownProps) => {
const dateItem = lookupDateById(ownProps.dateId);
return {
StartDate: formatDate(dateItem.StartDate),
EndDate: calculateAndFormatEndDate(dateItem.EndDate),
NumberOfWeeks: dateItem.NumberOfWeeks
}
};
const datesTable = ({id}) => (
<table>
<tr>
<th>Start Date</th>
<th>End Date</th>
<th>Number of weeks</th>
</tr>
<DatesRowContainer dateId={id} />
</table>
);
const mapStateToPropsForTable = (state) => ({
dates: state.dates.map(x => {id: x.id})
});
const DatesTableContainer = connect(mapStateToPropsForTable)(datesTable);
最好的方法可能是在你的减速器中重新映射它。在那里,它只需要完成一次,而不是在 在每个渲染 上执行,如果你要在你的 mapStateToProps
- 或 render
- 中重新映射它职能。无论如何,您的数据获取(或从任何来源加载数据)已经是“慢动作”,因此如果在那里添加重新映射,不会打扰用户。
在最后两个中的任何一个中,您都可能使您的应用程序卡顿很多,尤其是在您的数据集很大或重新映射成本高昂的情况下。
然而,每次重新渲染都卡顿并不理想。
或者,您也可以使用 reselect 库,但我自己还没有用过,所以我不能对此发表任何评论。
我有一个相当简单的要求,即在我的 Javascript 应用程序中呈现 table 的 start/end-dates:
<table>
<tr>
<th>Start Date</th>
<th>End Date</th>
<th>Number of weeks</th>
</tr>
<tr>
<td>2 Jan 2017</td>
<td>9 Jan 2017</td>
<td>1</td>
</tr>
<!-- etc -->
</table>
我的原始数据是这样的:
[
{
Id: 8672,
StartDate: "2017-01-02",
Weeks: 1
}
// More rows
]
我的问题是执行这些字段的计算和格式设置以保持关注点分离而不会对性能产生不利影响的最佳方法是什么?我考虑了多种方法:
在将数据存储到数据存储之前对其进行转换
我可以使用 reducer 函数,或引发 "data loaded" 操作的代码片段,将原始数据映射到更接近于 UI 组件所需格式和字段的内容.这具有使 UI 组件简单的优点,但它确实将数据和 reducer 与用户界面控件非常紧密地联系在一起。
在UI组件中执行计算和格式化
我可以只格式化和解析用户 UI 组件中的数据。这似乎是个坏主意,因为它将 UI 组件与数据格式紧密联系在一起。
在 table UI
的 mapStateToProps 中执行转换这似乎是个好方法,但它会强制 react-redux 绑定在每次状态更改时重新渲染整个 table,因为每次渲染都会导致 dates
prop拥有不同的身份
const datesRow = (date) => (
<tr>
<td>{date.StartDate}</td>
<td>{date.EndDate}</td>
<td>{date.NumberOfWeeks}</td>
</tr>
)
const datesTable = ({dates}) => (
<table>
<tr>
<th>Start Date</th>
<th>End Date</th>
<th>Number of weeks</th>
</tr>
{ dates.map(datesRow) }
</table>
);
const mapStateToProps = (state) => ({
dates: state.dates.map(x => transformDates(x))
});
const DatesTableContainer = connect(mapStateToProps)(datesTable);
创建行 UI 组件并在那里进行映射
这似乎保持了 UI 和前一个选项提供的状态之间的松散耦合,但也允许 UI 的增量更新。容器似乎很重。这是个好主意吗?
const datesRow = (date) => (
<tr>
<td>{date.StartDate}</td>
<td>{date.EndDate}</td>
<td>{date.NumberOfWeeks}</td>
</tr>
)
const mapStateToPropsForRow = (state, ownProps) => {
const dateItem = lookupDateById(ownProps.dateId);
return {
StartDate: formatDate(dateItem.StartDate),
EndDate: calculateAndFormatEndDate(dateItem.EndDate),
NumberOfWeeks: dateItem.NumberOfWeeks
}
};
const DatesRowContainer = connect(mapStateToPropsForRow)(datesRow);
const mapStateToPropsForRow = (state, ownProps) => {
const dateItem = lookupDateById(ownProps.dateId);
return {
StartDate: formatDate(dateItem.StartDate),
EndDate: calculateAndFormatEndDate(dateItem.EndDate),
NumberOfWeeks: dateItem.NumberOfWeeks
}
};
const datesTable = ({id}) => (
<table>
<tr>
<th>Start Date</th>
<th>End Date</th>
<th>Number of weeks</th>
</tr>
<DatesRowContainer dateId={id} />
</table>
);
const mapStateToPropsForTable = (state) => ({
dates: state.dates.map(x => {id: x.id})
});
const DatesTableContainer = connect(mapStateToPropsForTable)(datesTable);
最好的方法可能是在你的减速器中重新映射它。在那里,它只需要完成一次,而不是在 在每个渲染 上执行,如果你要在你的 mapStateToProps
- 或 render
- 中重新映射它职能。无论如何,您的数据获取(或从任何来源加载数据)已经是“慢动作”,因此如果在那里添加重新映射,不会打扰用户。
在最后两个中的任何一个中,您都可能使您的应用程序卡顿很多,尤其是在您的数据集很大或重新映射成本高昂的情况下。 然而,每次重新渲染都卡顿并不理想。
或者,您也可以使用 reselect 库,但我自己还没有用过,所以我不能对此发表任何评论。