【发布时间】:2020-04-25 08:23:35
【问题描述】:
我有一个 React / Redux 应用程序,我在其中绘制了一个二维网格。网格数据在 Redux 存储中,作为数组的数组。例如对于 3x2 网格,它可能是这样的:
state.grid = [
[{value: 1},{value: 2}],
[{value: 0},{value: 1}],
[{value: 1},{value: 3}]
]
用户可以单击方格来更改其值。由于网格可能很大,我只想更新已更改的单元格。但无论我尝试什么,整个网格都会更新。
我使用reselect 和re-reselect 来记忆各个选择器,但我认为它不起作用。这是我的代码的简化版本:
GridCell.js:
class GridCell extends PureComponent {
render() {
const { valueObj } = this.props;
return <span>valueObj.value</span>
}
function mapStateToProps(state, ownProps) {
const { rowIndex, columnIndex } = ownProps;
const valueObj = getValueCached(state, `${columnIndex}_${rowIndex}`);
console.log('which cell', `${columnIndex}_${rowIndex}`);
return {valueObj};
}
export default connect(mapStateToProps)(GridCell);
还有一个父组件绘制 GridCells 的网格,将其 columnIndex 和 rowIndex 作为 props 传递给每个组件:
class MyGrid extends PureComponent {
const columns = 3;
const rows = 2;
render() {
columns.map((column) => {
rows.map((row) => {
<GridCell
columnIndex={column}
rowIndex={row}
/>
}
}
在我的 Redux 商店中:
import createCachedSelector from 're-reselect';
export const getValueFromCache = (state, identifier) => {
const identifiers = identifier.split('_');
return state.grid[identifiers[0]][identifiers[1]];
};
export const getValueCached = createCachedSelector(
getValueFromCache,
// resultFunc
(value) => value,
)(
// re-reselect keySelector (receives selectors' arguments)
// Use "columnIndex_rowIndex" as cacheKey
// re-reselect only accepts string as cacheKey
(_state_, identifier) => identifier
);
当用户单击一个网格方块时,mapStateToProps 函数中的“哪个单元格”日志会针对每个网格单元格写入控制台。
编辑:我在减速器中使用updeep 来更新值,如下所示:
case UPDATE_GRID_CELL: {
{ newValue } = action.payload;
return updeep({
'grid': { [columnIndex]: { [rowIndex]: newValue } },
state);
}
我还尝试通过 connect 将值作为简单的 prop 获取:
function mapStateToProps(state, ownProps) {
const { rowIndex, columnIndex } = ownProps;
return {state.grid[columnIndex][rowIndex].value};
}
我已经检查了是否有任何道具或状态发生了变化:
componentDidUpdate(prevProps, prevState) {
console.log('componentWillUpdate');
console.log('new', JSON.stringify(this.props));
console.log('old', JSON.stringify(prevProps));
console.log('changed props', JSON.stringify(this.props) !== JSON.stringify(prevProps));
console.log('state');
console.log('new', JSON.stringify(this.state));
console.log('old', JSON.stringify(prevState));
console.log('changed state', JSON.stringify(this.state) !== JSON.stringify(prevState));
}
这会记录错误,但 render() 方法会运行,如控制台日志所示。我很困惑,因为我认为 pureComponent 的意义在于它只有在其状态或道具发生变化时才会呈现?
我希望 memoization 为未受影响的单元格提供相同的值,并且不会触发组件更新。但我一定是错过了什么。
我不知道如何测试 memoization 是否有效。如果我在 getValueCached() 中放置一个 console.log,它会为每个单元格写入,但我不知道它是否会这样做......?
有什么方法可以防止每个网格单元组件在单个值更改时更新?感谢您的任何建议。
编辑:从 cmets / answer 和进一步调查来看,重新渲染似乎是由数组道具引起的。我创建了一个new question,用一个最小的沙箱示例说明了这一点。
【问题讨论】:
-
为什么不连接而不是连接
MyGrid而是将原始值作为简单的道具传递给GridCell组件?我想说将每个GridCell包装在connect()中是这里的关键问题。 -
您能告诉我们,您是如何更新这些值的吗?
-
有时 redux 太“复杂”,不允许你做简单的事情。也许github.com/aralroca/fragmented-store 可以帮助您,认为如果您更新商店中的某个字段,则只有使用该特定字段的组件会被重新渲染,而不是商店的其余部分。
标签: reactjs optimization reselect