如何将一维数组转为二维数组并存储到React Native状态中?
问题分析与解决方案
一、一维转二维的效率优化
你原来的转换代码遍历了两次数组来生成行、列两个二维数组,其实可以一次遍历完成两个结构的构建,把时间复杂度从O(2n)降到O(n),效率提升明显。
先看你原来的代码:
for (let i = 0; i < this.state.array.length; i++) { let row = this.state.array[i].row; if (newSeats[row] === undefined) newSeats[row] = []; newSeats[row].push(this.state.array[i]); } for (let i = 0; i < this.state.array.length; i++) { let column = this.state.array[i].column; if (newSeatss[column] === undefined) newSeatss[column] = []; newSeatss[column].push(this.state.array[i]); }
优化后的单次遍历版本:
const convertTo2D = (seats) => { const rowBased = []; const columnBased = []; seats.forEach(seat => { const { row, column } = seat; // 构建行维度的二维数组 if (!rowBased[row]) rowBased[row] = []; rowBased[row].push(seat); // 同时构建列维度的二维数组 if (!columnBased[column]) columnBased[column] = []; columnBased[column].push(seat); }); return { rowBased, columnBased }; }
另外你用reduce构建行二维数组的思路是对的,但要注意:如果你的row是字符串类型(比如数据里的"0""1"),数组会自动把索引转成数字,但如果出现非连续的row值(比如"a""b"),用对象存储会更稳妥;如果row是连续数字字符串,用数组没问题。
二、解决setState"最大调用深度超限"错误
这个错误的核心原因是你在render函数里直接调用了会触发setState的方法,导致render → 调用方法 → setState → 重新render → 再次调用方法的无限循环。
看你的代码,当前seats()方法只是console.log,但如果之前你在这个方法里写了setState({ seatArray: arr2d }),就会触发无限循环。正确的做法是:
- 不要在render里执行修改state的逻辑,应该在数据获取完成后(比如
componentDidMount的回调)完成转换并设置state。 - 如果后续
selectedSeatLayouts会变化,用componentDidUpdate监听变化并重新转换。
修改后的componentDidMount示例:
componentDidMount() { this.props.fetchSeatLayout(this.props.selectedBus.id, (data) => { // 在这里完成一维转二维的转换,再存入state const { rowBased, columnBased } = convertTo2D(data.seats); this.setState({ isLoading : false, selectedSeatLayouts: data.seats, rowBasedSeats: rowBased, columnBasedSeats: columnBased }) }) }
如果后续selectedSeatLayouts可能更新,补充componentDidUpdate:
componentDidUpdate(prevProps, prevState) { // 当selectedSeatLayouts变化时,重新转换并更新state if (prevState.selectedSeatLayouts !== this.state.selectedSeatLayouts) { const { rowBased, columnBased } = convertTo2D(this.state.selectedSeatLayouts); this.setState({ rowBasedSeats: rowBased, columnBasedSeats: columnBased }); } }
之后在render里直接用state里的转换后数组即可,比如用FlatList渲染行:
render() { const { rowBasedSeats } = this.state; return( <View style={styles.container}> <Loader loading={this.state.isLoading} /> <View style={{ flex: 1 }}> <View style={styles.headerContainer}> <View style={{ paddingTop: 50, paddingHorizontal: 20, alignItems: 'center' }}> <Text>30 Sep, 2020</Text> </View> </View> <View style={styles.bodyContainer}> <FlatList data={rowBasedSeats.filter(Boolean)} {/* 过滤空行(如果row不连续) */} renderItem={({ item }) => ( <View style={{ flexDirection: 'row', gap: 8, padding: 4 }}> {item.map(seat => ( <TouchableOpacity key={`${seat.row}-${seat.column}`} style={{ padding: 12, backgroundColor: '#f0eff4', borderRadius: 4 }} > <Text>{seat.name}</Text> </TouchableOpacity> ))} </View> )} keyExtractor={(item, index) => index.toString()} /> </View> </View> </View> ) }
三、额外优化建议
- 把字符串类型的row/column转为数字:用
parseInt(row, 10)转换,避免数组出现不必要的空槽(比如row是"2"时,数组0、1索引会是undefined)。 - 去重处理:如果seat数据可能存在重复的row+column组合,转换前先做去重,避免二维数组出现重复项。
- 迁移到函数组件+hooks:类组件可以逐步换成函数组件,用
useState和useEffect处理状态和副作用会更简洁,示例如下:
const SeatLayout = ({ selectedBus, fetchSeatLayout }) => { const [isLoading, setIsLoading] = useState(true); const [selectedSeatLayouts, setSelectedSeatLayouts] = useState([]); const [rowBasedSeats, setRowBasedSeats] = useState([]); const [columnBasedSeats, setColumnBasedSeats] = useState([]); // 初始获取座位数据 useEffect(() => { fetchSeatLayout(selectedBus.id, (data) => { setSelectedSeatLayouts(data.seats); const { rowBased, columnBased } = convertTo2D(data.seats); setRowBasedSeats(rowBased); setColumnBasedSeats(columnBased); setIsLoading(false); }); }, [selectedBus.id, fetchSeatLayout]); // 监听座位数据变化,重新转换 useEffect(() => { if (selectedSeatLayouts.length > 0) { const { rowBased, columnBased } = convertTo2D(selectedSeatLayouts); setRowBasedSeats(rowBased); setColumnBasedSeats(columnBased); } }, [selectedSeatLayouts]); // 渲染部分... }
内容的提问来源于stack exchange,提问作者hellodevs
相关产品推荐
相关产品推荐

