React useState未正确更新子组件props导致表格渲染延迟求助
React状态更新后表格未即时渲染的问题修复
问题现象
父组件通过子组件1的选择操作更新trackerSetup状态,再将该状态传递给子组件2渲染表格。但选择新值时表格无反应,直到下一次选择才会显示上一次对应的数据。
问题根源与修复步骤
1. 表格组件的useEffect未监听props变化
TrackerTableComponent里的useEffect依赖数组为空,仅在组件首次挂载时执行一次数据请求。当props.trackerSetup更新时,不会重新触发数据获取,导致表格展示旧数据。
修复代码:
useEffect(() => { const loadTrackerData = async () => { setLoading(true); // 添加可选链避免空值报错 const fetchedTrackers = await getTrackerObject( props.trackerSetup?.trackers || [] ); console.log("Displaying the following trackers ", fetchedTrackers); setTrackers(fetchedTrackers); // 异步请求完成后再关闭loading状态 setLoading(false); }; loadTrackerData(); }, [props.trackerSetup]); // 监听trackerSetup变化
2. 异步逻辑中loading状态更新时机错误
原代码中setLoading(false)直接写在异步函数调用之后,会在请求还未完成时就将loading设为false,导致表格提前渲染旧状态。必须把setLoading(false)移到await操作完成后。
3. 子组件1的useMemo误用
useMemo的设计目的是缓存计算结果,不应该用来执行setState这类副作用操作。这里应该替换为useEffect:
修复代码:
useEffect(() => { const existingSetups = JSON.parse( localStorage.getItem("trackerSetup")! ) as TrackerSetup[]; setExistingTrackerSetups(existingSetups); }, []);
4. 可选链优化(可选)
在访问props.trackerSetup.trackers时添加可选链,避免trackerSetup未初始化时抛出空值错误,提升代码健壮性。
完整代码参考
父组件
const MainComponent = () => { const [trackerSetup, setTrackerSetup] = useState<TrackerSetup>(); return ( <> <Container maxWidth="lg"> <Grid direction="row" container alignItems="center" justifyContent="center" spacing={2} marginTop={5} > <Grid item xs={6}> <AddTrackerSetupComponent /> </Grid> <Grid item xs={6}> <SelectTrackerSetupComponent setTrackerSetup={setTrackerSetup} /> </Grid> </Grid> {trackerSetup ? ( <TrackerTableComponent trackerSetup={trackerSetup} /> ) : ( <span>选择一个追踪器配置</span> )} </Container> </> ); }; export default MainComponent;
子组件1(选择组件)
interface SelectTrackerSetupComponentProps { setTrackerSetup: React.Dispatch<React.SetStateAction<TrackerSetup | undefined>>; } const SelectTrackerSetupComponent = ( props: SelectTrackerSetupComponentProps ) => { const [existingTrackerSetups, setExistingTrackerSetups] = useState<TrackerSetup[]>(); const [ selectedExistingTrackerSetupIndex, setSelectedExistingTrackerSetupIndex, ] = useState(0); useEffect(() => { const existingSetups = JSON.parse( localStorage.getItem("trackerSetup")! ) as TrackerSetup[]; setExistingTrackerSetups(existingSetups); }, []); return ( <> {existingTrackerSetups && existingTrackerSetups.length > 0 ? ( <FormControl fullWidth> <InputLabel id="existingSetups">选择已有配置</InputLabel> <Select labelId="existingSetups" id="existingSetups-select" label="已有配置" value={selectedExistingTrackerSetupIndex} onChange={(e) => { const selectedIndex = Number(e.target.value); setSelectedExistingTrackerSetupIndex(selectedIndex); props.setTrackerSetup(existingTrackerSetups[selectedIndex]); }} > {existingTrackerSetups.map((trackerSetup: TrackerSetup, index) => { return ( <MenuItem key={trackerSetup.name} value={index}> {trackerSetup.name} </MenuItem> ); })} </Select> </FormControl> ) : ( <span>加载中..</span> )} </> ); }; export default SelectTrackerSetupComponent;
子组件2(表格组件)
interface TrackerTableComponentProps { trackerSetup: TrackerSetup; } const TrackerTableComponent = (props: TrackerTableComponentProps) => { const [trackers, setTrackers] = useState<Tracker[]>(); const [loading, setLoading] = useState(false); useEffect(() => { const loadTrackerData = async () => { setLoading(true); const fetchedTrackers = await getTrackerObject( props.trackerSetup?.trackers || [] ); console.log("Displaying the following trackers ", fetchedTrackers); setTrackers(fetchedTrackers); setLoading(false); }; loadTrackerData(); }, [props.trackerSetup]); return ( <> {!loading ? ( <TableContainer component={Paper}> <Table sx={{ minWidth: 650 }} aria-label="simple table"> <TableHead> <TableRow> <TableCell>追踪器ID</TableCell> <TableCell>固件版本</TableCell> <TableCell>电池电量</TableCell> <TableCell>操作</TableCell> </TableRow> </TableHead> <TableBody> {trackers && trackers.map((tracker: Tracker) => { console.log("Table: displaying tracker ", tracker); return ( <TableRow key={tracker.id} sx={{ "&:last-child td, &:last-child th": { border: 0 } }} > <TableCell component="th" scope="row"> {tracker.id} </TableCell> <TableCell>{tracker.fwVersion}</TableCell> <TableCell>{tracker.batteryLevel.toString()} %</TableCell> <TableCell> <Button variant="contained" color="success" size="small" > 更新固件 </Button> </TableCell> </TableRow> ); })} </TableBody> </Table> </TableContainer> ) : ( <span>加载中...</span> )} </> ); }; export default TrackerTableComponent;
内容的提问来源于stack exchange,提问作者anass arakia
相关产品推荐
相关产品推荐

