Material UI如何创建带行列标题的5×4输入表格?
问题描述
我想用Material UI创建一个带行标题、列标题的5×4输入表格,预期效果参考指定图片(示例为5×3,需调整为5×4)。但当前代码存在两个问题:
- 布局不符合预期
- 输入
umaValues[0][0]时整列会同步变化
以下是我的代码片段:
<Grid item xs={12}> <Grid container spacing={2}> <Grid item xs={3}> <FormControl> <FormLabel>1st Place</FormLabel> <TextField variant="outlined" type="number" value={umaValues[0][0]} onChange={handleUmaChange(0, 0)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel>2nd Place</FormLabel> <TextField variant="outlined" type="number" value={umaValues[0][1]} onChange={handleUmaChange(0, 1)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel>3rd Place</FormLabel> <TextField variant="outlined" type="number" value={umaValues[0][2]} onChange={handleUmaChange(0, 2)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel>4th Place</FormLabel> <TextField variant="outlined" type="number" value={umaValues[0][3]} onChange={handleUmaChange(0, 3)} /> </FormControl> </Grid> </Grid> <Grid container spacing={2}> <Grid item xs={3}> <FormControl> <FormLabel>Uma 0 Over</FormLabel> <TextField variant="outlined" type="number" value={umaValues[1][0]} onChange={handleUmaChange(1, 0)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel>Uma 1 Over</FormLabel> <TextField variant="outlined" type="number" value={umaValues[1][1]} onChange={handleUmaChange(1, 1)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel>Uma 2 Over</FormLabel> <TextField variant="outlined" type="number" value={umaValues[1][2]} onChange={handleUmaChange(1, 2)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel>Uma 3 Over</FormLabel> <TextField variant="outlined" type="number" value={umaValues[1][3]} onChange={handleUmaChange(1, 3)} /> </FormControl> </Grid> </Grid> <Grid container spacing={2}> <Grid item xs={3}> <FormControl> <FormLabel>Uma 4 Over</FormLabel> <TextField variant="outlined" type="number" value={umaValues[2][0]} onChange={handleUmaChange(2, 0)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel></FormLabel> <TextField variant="outlined" type="number" value={umaValues[2][1]} onChange={handleUmaChange(2, 1)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel></FormLabel> <TextField variant="outlined" type="number" value={umaValues[2][2]} onChange={handleUmaChange(2, 2)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel></FormLabel> <TextField variant="outlined" type="number" value={umaValues[2][3]} onChange={handleUmaChange(2, 3)} /> </FormControl> </Grid> </Grid> <Grid container spacing={2}> <Grid item xs={3}> <FormControl> <FormLabel></FormLabel> <TextField variant="outlined" type="number" value={umaValues[3][0]} onChange={handleUmaChange(3, 0)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel></FormLabel> <TextField variant="outlined" type="number" value={umaValues[3][1]} onChange={handleUmaChange(3, 1)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel></FormLabel> <TextField variant="outlined" type="number" value={umaValues[3][2]} onChange={handleUmaChange(3, 2)} /> </FormControl> </Grid> <Grid item xs={3}> <FormControl> <FormLabel></FormLabel> <TextField variant="outlined" type="number" value={umaValues[3][3]} onChange={handleUmaChange(3, 3)} /> </FormControl> </Grid> </Grid> </Grid>
解决方案
1. 修复整列同步变化的问题
整列同步变化的核心原因是**handleUmaChange写法错误**:直接调用handleUmaChange(0,0)会导致组件渲染时就执行该函数,且同列输入框共享了同一状态更新逻辑。正确写法是让handleUmaChange返回一个函数,而非直接执行。
修改后的handleUmaChange示例:
const handleUmaChange = (row, col) => (e) => { const newValue = Number(e.target.value); // 深拷贝原数组,避免直接修改状态 const newUmaValues = umaValues.map(rowArr => [...rowArr]); newUmaValues[row][col] = newValue; setUmaValues(newUmaValues); };
注意:必须深拷贝二维数组,否则React无法检测到状态变化,导致输入框值不更新。直接修改umaValues[row][col]属于修改原状态对象,React不会触发重渲染。
2. 调整布局实现标准行列标题结构
当前代码把列标题放在第一行的FormLabel里,行标题分散在各行第一列,布局逻辑混乱。重构为表头行+数据行的结构,每一行包含一个行标题单元格和4个输入单元格:
import { Grid, FormControl, FormLabel, TextField } from '@mui/material'; import { useState } from 'react'; // 定义行列标题数据 const columnTitles = ['1st Place', '2nd Place', '3rd Place', '4th Place']; const rowTitles = ['Uma 0 Over', 'Uma 1 Over', 'Uma 2 Over', 'Uma 3 Over', 'Uma 4 Over']; // 5行标题 export default function UmaTable() { // 初始化状态:5行4列的二维数组,初始值设为0 const [umaValues, setUmaValues] = useState( Array(5).fill().map(() => Array(4).fill(0)) ); // 正确的状态更新函数 const handleUmaChange = (row, col) => (e) => { const newValue = Number(e.target.value); const newUmaValues = umaValues.map(r => [...r]); newUmaValues[row][col] = newValue; setUmaValues(newUmaValues); }; return ( <Grid container spacing={2} sx={{ mt: 2 }}> {/* 表头行:第一列为空占位,后面是4个列标题 */} <Grid item xs={12}> <Grid container spacing={2}> <Grid item xs={3} /> {columnTitles.map((title, col) => ( <Grid item xs={3} key={col}> <FormLabel sx={{ fontWeight: 'bold', textAlign: 'center', display: 'block' }}> {title} </FormLabel> </Grid> ))} </Grid> </Grid> {/* 数据行:每行包含一个行标题 + 4个输入框 */} {rowTitles.map((title, row) => ( <Grid item xs={12} key={row}> <Grid container spacing={2}> {/* 行标题单元格 */} <Grid item xs={3}> <FormLabel sx={{ textAlign: 'right', display: 'flex', alignItems: 'center', height: '100%' }}> {title} </FormLabel> </Grid> {/* 4个输入框单元格 */} {Array(4).fill().map((_, col) => ( <Grid item xs={3} key={col}> <FormControl fullWidth> <TextField variant="outlined" type="number" value={umaValues[row][col]} onChange={handleUmaChange(row, col)} /> </FormControl> </Grid> ))} </Grid> </Grid> ))} </Grid> ); }
布局调整说明:
- 用
Grid构建完整表格结构,第一行是列标题,后续每行是行标题+输入框 - 行标题固定在每行左侧,列标题固定在顶部
- 使用循环渲染代替重复代码,更易维护,避免手动编写重复的Grid项
- 通过
sx样式调整标题对齐方式,让布局更规整
内容的提问来源于stack exchange,提问作者Vincent Zheng
相关产品推荐
相关产品推荐

