React中RTK Query顺序执行查询:处理数据依赖问题
RTK Query 顺序执行依赖查询的解决方案
问题背景
在React项目中使用RTK Query时,存在两个关联查询:
useGetProfileByUserIdQuery:根据用户ID获取用户信息,其中包含classIduseGetScheduleByClassIdAndDayQuery:需要classId作为参数获取对应班级的日程
初始渲染时两个查询会同时执行,导致第二个查询的classId为undefined,尝试使用skip参数但未完全解决问题。
原核心代码片段
const currentUser = useSelector(selectCurrentUser); const userId = currentUser.userId; const { data: profileData } = useGetProfileByUserIdQuery(userId); const classId = profileData?.data?.class?.id; const { data: schedules, isLoading: getSchedulesLoading } = useGetScheduleByClassIdAndDayQuery({ classId, day: selectedDay });
推荐方案:精准使用skip参数+监听前置查询状态
RTK Query的skip参数是处理依赖查询的最优方案,之前的问题在于skip条件不够严谨,未结合前置查询的状态判断。
1. 完善前置查询的状态监听
首先获取useGetProfileByUserIdQuery的加载状态和成功状态,确保只有当查询成功返回数据后,才执行后续查询:
const { data: profileData, isLoading: profileLoading, isSuccess: profileSuccess } = useGetProfileByUserIdQuery(userId); const classId = profileData?.data?.class?.id;
2. 设置第二个查询的skip条件
skip的触发条件应为:当classId不存在,或前置查询未成功完成时,跳过第二个查询:
const { data: schedules, isLoading: getSchedulesLoading } = useGetScheduleByClassIdAndDayQuery( { classId, day: selectedDay }, { skip: !classId || !profileSuccess } );
3. 优化组件渲染逻辑
在UI中优先处理前置查询的加载状态,提升用户体验:
// 先展示profile加载状态 {profileLoading ? ( <Box style={{ display: "flex", flexDirection: "column", alignItems: "center", padding: "20px" }}> <CircularProgress /> <Typography variant="subtitle1" align="center"> Memuat profil... </Typography> </Box> ) : getSchedulesLoading ? ( // 再展示日程加载状态 <Box style={{ display: "flex", flexDirection: "column", alignItems: "center", padding: "20px" }}> <CircularProgress /> <Typography variant="subtitle1" align="center"> Memuat jadwal... </Typography> </Box> ) : schedules?.length ? ( // 展示日程列表 <Box> {schedules.map((schedule) => ( <div key={schedule.id}> <Typography variant="subtitle1"> {schedule.day}: {formatTime(schedule.start_time)} - {formatTime(schedule.end_time)} </Typography> <Typography variant="body2"> Guru: {schedule.teacher.full_name}, Kelas: {schedule.class.name}, Mata Pelajaran: {schedule.subject.name} </Typography> </div> ))} </Box> ) : ( // 无数据状态 <Box style={{ display: "flex", flexDirection: "column", alignItems: "center", padding: "20px" }}> <Typography variant="subtitle1" align="center" gutterBottom> Tidak ada data. </Typography> </Box> )}
完整优化后的组件代码
import { Box, CircularProgress, Container, Paper, Tab, Tabs, Typography, } from "@mui/material"; import { useState } from "react"; import { useGetScheduleByClassIdAndDayQuery } from "../schedulesApiSlice"; import { useSelector } from "react-redux"; import { selectCurrentUser } from "../../auth/authSlice"; import { useGetProfileByUserIdQuery } from "../../profile/profileApiSlice"; const formatTime = (dateString) => { const options = { hour: "numeric", minute: "numeric" }; return new Date(dateString).toLocaleTimeString(undefined, options); }; const StudentScheduleList = () => { const [value, setValue] = useState(0); const [selectedDay, setSelectedDay] = useState("Senin"); const currentUser = useSelector(selectCurrentUser); const userId = currentUser.userId; // 监听前置查询的状态 const { data: profileData, isLoading: profileLoading, isSuccess: profileSuccess } = useGetProfileByUserIdQuery(userId); const classId = profileData?.data?.class?.id; // 设置skip条件控制查询执行时机 const { data: schedules, isLoading: getSchedulesLoading } = useGetScheduleByClassIdAndDayQuery( { classId, day: selectedDay }, { skip: !classId || !profileSuccess } ); const handleChange = (event, newValue) => { setValue(newValue); const selectedDayLabel = [ "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu" ][newValue]; setSelectedDay(selectedDayLabel); }; return ( <Container sx={{ width: "100%" }}> <Typography variant="h4" gutterBottom> Daftar Jadwal </Typography> <Paper> <Box sx={{ maxWidth: { xs: 320, sm: 480, md: 720, lg: 960, xl: 1280 }, bgcolor: "background.paper", marginX: "auto", }} > <Tabs value={value} onChange={handleChange} variant="scrollable" scrollButtons allowScrollButtonsMobile aria-label="scrollable" > <Tab label="Senin" /> <Tab label="Selasa" /> <Tab label="Rabu" /> <Tab label="Kamis" /> <Tab label="Jumat" /> <Tab label="Sabtu" /> <Tab label="Minggu" /> </Tabs> {/* 分层处理加载状态 */} {profileLoading ? ( <Box style={{ display: "flex", flexDirection: "column", alignItems: "center", padding: "20px" }}> <CircularProgress /> <Typography variant="subtitle1" align="center"> Memuat profil... </Typography> </Box> ) : getSchedulesLoading ? ( <Box style={{ display: "flex", flexDirection: "column", alignItems: "center", padding: "20px" }}> <CircularProgress /> <Typography variant="subtitle1" align="center"> Memuat jadwal... </Typography> </Box> ) : schedules?.length ? ( <Box> {schedules.map((schedule) => ( <div key={schedule.id}> <Typography variant="subtitle1"> {schedule.day}: {formatTime(schedule.start_time)} - {formatTime(schedule.end_time)} </Typography> <Typography variant="body2"> Guru: {schedule.teacher.full_name}, Kelas: {schedule.class.name}, Mata Pelajaran: {schedule.subject.name} </Typography> </div> ))} </Box> ) : ( <Box style={{ display: "flex", flexDirection: "column", alignItems: "center", padding: "20px" }}> <Typography variant="subtitle1" align="center" gutterBottom> Tidak ada data. </Typography> </Box> )} </Box> </Paper> </Container> ); }; export default StudentScheduleList;
可选方案:使用useLazyQuery手动触发查询
如果需要更灵活的触发逻辑(比如结合其他状态变化),可以使用useLazyQuery,在前置查询成功后手动调用:
import { useLazyQuery } from '@reduxjs/toolkit/query/react'; // ... const [getSchedules, { data: schedules, isLoading: getSchedulesLoading }] = useLazyQuery(useGetScheduleByClassIdAndDayQuery); useEffect(() => { // 当前置查询成功且classId存在时,触发日程查询 if (profileSuccess && classId) { getSchedules({ classId, day: selectedDay }); } }, [profileSuccess, classId, selectedDay, getSchedules]);
这种方式适合复杂场景,但skip参数方案更简洁,是RTK Query官方推荐的依赖查询处理方式。
内容的提问来源于stack exchange,提问作者Kartono bin Saleh
相关产品推荐
相关产品推荐

