React组件按条件初始化计数器生成唯一Widget ID的问题
解决react-grid-layout动态添加组件时的唯一ID生成与持久化问题
我看了你基于react-grid-layout的组件代码,已经帮你调整好了onAddItem函数的逻辑,同时修复了localStorage持久化的问题,确保ID不会重复报错。下面是修改后的完整代码:
import React from "react"; import ReactDOM from "react-dom"; import { WidthProvider, Responsive } from "react-grid-layout"; import _ from "lodash"; const ResponsiveReactGridLayout = WidthProvider(Responsive); const originalLayout = getFromLS("layout") || []; /** * This layout demonstrates how to use a grid with a dynamic number of elements. */ class AddRemoveLayout extends React.PureComponent { static defaultProps = { className: "layout", cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }, rowHeight: 100, onLayoutChange: function(){} }; constructor(props) { super(props); // 从localStorage读取持久化的widgetID数组,为空则初始化空数组 const savedWidgetIDs = getFromLS("widget-id") || []; this.state = { items: originalLayout.map((i, key, list) => { return { i: i.i.toString(), x: i.x, y: i.y, w: i.w, h: i.h, add: i.i === (list.length - 1).toString() }; }), newCounter: savedWidgetIDs.length > 0 ? savedWidgetIDs[savedWidgetIDs.length - 1] + 1 : 0, layout: JSON.parse(JSON.stringify(originalLayout)) }; this.widgetID = savedWidgetIDs; this.onAddItem = this.onAddItem.bind(this); this.onBreakpointChange = this.onBreakpointChange.bind(this); this.onLayoutChange = this.onLayoutChange.bind(this); } onAddItem() { let newItemId; let updatedWidgetIDs; if (this.widgetID.length === 0) { // 数组为空时,用newCounter生成ID newItemId = `n${this.state.newCounter}`; updatedWidgetIDs = [...this.widgetID, this.state.newCounter]; this.setState(prevState => ({ items: prevState.items.concat({ i: newItemId, x: (prevState.items.length * 2) % (this.props.cols.lg || 12), y: Infinity, // 放在底部 w: 2, h: 2 }), newCounter: prevState.newCounter + 1 })); } else { // 数组不为空时,取最后一个值递增生成ID const lastId = this.widgetID[this.widgetID.length - 1]; const nextId = lastId + 1; newItemId = `n${nextId}`; updatedWidgetIDs = [...this.widgetID, nextId]; this.setState(prevState => ({ items: prevState.items.concat({ i: newItemId, x: (prevState.items.length * 2) % (this.props.cols.lg || 12), y: Infinity, w: 2, h: 2 }), newCounter: nextId + 1 })); } // 更新widgetID数组并持久化到localStorage this.widgetID = updatedWidgetIDs; saveToLS("widget-id", this.widgetID); } render() { return ( <div> <button onClick={this.onAddItem}>Add Item</button> <ResponsiveReactGridLayout onLayoutChange={(layout) => this.onLayoutChange(layout) } > {_.map(this.state.items, el => this.createElement(el))} </ResponsiveReactGridLayout> </div> ); } } // 通用localStorage存储函数 function saveToLS(key, value) { if (global.localStorage) { global.localStorage.setItem(key, JSON.stringify(value)); } } // 通用localStorage读取函数 function getFromLS(key) { let value = []; if (global.localStorage) { try { value = JSON.parse(global.localStorage.getItem(key)) || []; } catch (e) { /* Ignore parsing errors */ } } return value; } export default AddRemoveLayout;
关键改动说明:
- 修正localStorage读写逻辑:简化了
saveToLS和getFromLS函数,确保widgetID数组能正确存入和读取,避免原来的参数混乱问题。 - 初始化时读取持久化数据:在constructor里从localStorage读取之前保存的widgetID,初始化组件时就能继承之前的ID序列,不会刷新后重置。
- 分情况生成唯一ID:
- 当widgetID为空时,用
newCounter生成n{newCounter}格式的ID,同时更新counter和widgetID数组 - 当widgetID不为空时,取数组最后一个值加1生成新ID,同步更新counter和widgetID数组
- 当widgetID为空时,用
- 确保每次添加后持久化:每次添加新组件后,立即把更新后的widgetID数组存入localStorage,刷新页面后不会丢失序列。
- 避免state更新冲突:使用函数式更新
setState(prevState => {...})来获取最新的state值,避免异步更新导致的错误。
内容的提问来源于stack exchange,提问作者Aessandro




