如何访问Firestore引用文档数据?渲染异常求助
问题分析与解决方案
核心问题点
- 无限渲染/闪烁:直接在渲染阶段的
map循环中调用getData异步函数,每次组件渲染都会触发状态更新,进而引发重新渲染,形成循环。 - 状态覆盖:单一的
exerciseData状态会被多个exercise请求覆盖,导致所有exercise条目显示相同的最新数据,无法对应到各自的关联文档。 - 异步状态不同步:
getWorkouts中调用workouts.map时,setWorkouts是异步操作,此时workouts仍为旧状态,无法正确遍历新获取的训练数据。 - 变量初始化问题:组件内的
workoutsArray是普通变量,每次渲染都会重新初始化,可能导致数据异常。
解决方案一:预加载所有关联Exercise数据(推荐)
在获取Workouts数据时,直接批量加载关联的Exercise文档并整合到Workouts对象中,避免渲染阶段的异步请求。
修改后的完整代码
export function WorkoutDisplayScreen({ navigation }) { const [workouts, setWorkouts] = React.useState([]); const getWorkouts = async () => { console.log('Firebase READ & WRITE'); const workoutsCollection = collection(db, 'Users', auth.currentUser.uid, 'Workouts'); const workoutsSnapshot = await getDocs(workoutsCollection); // 批量整合Workout与关联的Exercise数据 const workoutsData = await Promise.all( workoutsSnapshot.docs.map(async (workoutDoc) => { const workout = workoutDoc.data(); // 获取当前Workout下所有Exercise的文档数据 const exercisesData = await Promise.all( workout.exercises.map(async (exerciseRef) => { const exerciseDoc = await getDoc(exerciseRef); return exerciseDoc.data(); }) ); return { ...workout, exercises: exercisesData }; }) ); setWorkouts(workoutsData); }; useEffect(() => { console.log('Rendering...'); getWorkouts(); }, []); return ( <ScreenContainer> <View style={styles.headerrow}> <TouchableOpacity onPress={() => navigation.goBack()}> <AntDesign name="leftcircle" size={24} color="black" style={styles.headericon} /> </TouchableOpacity> <Text style={styles.header}>My Workouts</Text> </View> {workouts.length > 0 && workouts.map((workout) => ( <View key={workout.id}> <View style={styles.workoutnamecontainer}> <Text style={styles.workoutnametext}>{workout.name}</Text> </View> {workout.exercises.map((exercise, index) => ( <View style={styles.exercisecontainer} key={index}> <Text style={styles.exercisetext}>{exercise.name}</Text> <Text style={styles.exercisetext}>{exercise.type}</Text> <Text style={styles.exercisetext}>{exercise.weight}</Text> <Text style={styles.exercisetext}>{exercise.reps}</Text> </View> ))} </View> ))} </ScreenContainer> ); }
解决方案二:按需加载Exercise数据(适合大数据量场景)
使用对象存储每个Exercise的文档数据,通过useEffect在Workouts数据更新时触发加载,避免一次性请求所有数据。
修改后的核心代码片段
export function WorkoutDisplayScreen({ navigation }) { const [workouts, setWorkouts] = React.useState([]); const [exerciseDataMap, setExerciseDataMap] = useState({}); // 存储Exercise ID与对应数据的映射 const getWorkouts = async () => { console.log('Firebase READ & WRITE'); const workoutsCollection = collection(db, 'Users', auth.currentUser.uid, 'Workouts'); const workoutsSnapshot = await getDocs(workoutsCollection); const workoutsData = workoutsSnapshot.docs.map(workoutDoc => workoutDoc.data()); setWorkouts(workoutsData); }; // 当Workouts更新时,加载未获取的Exercise数据 useEffect(() => { const loadExercises = async () => { const newExerciseMap = {...exerciseDataMap}; await Promise.all( workouts.flatMap(workout => workout.exercises.map(async (exerciseRef) => { const exerciseId = exerciseRef.id; if (!newExerciseMap[exerciseId]) { const exerciseDoc = await getDoc(exerciseRef); newExerciseMap[exerciseId] = exerciseDoc.data(); } }) ) ); setExerciseDataMap(newExerciseMap); }; if (workouts.length > 0) loadExercises(); }, [workouts]); // 渲染部分 return ( <ScreenContainer> {/* 头部代码省略 */} {workouts.length > 0 && workouts.map((workout) => ( <View key={workout.id}> <View style={styles.workoutnamecontainer}> <Text style={styles.workoutnametext}>{workout.name}</Text> </View> {workout.exercises.map((exerciseRef, index) => { const exercise = exerciseDataMap[exerciseRef.id]; if (!exercise) return <Text key={index}>加载中...</Text>; return ( <View style={styles.exercisecontainer} key={index}> <Text style={styles.exercisetext}>{exercise.name}</Text> <Text style={styles.exercisetext}>{exercise.type}</Text> <Text style={styles.exercisetext}>{exercise.weight}</Text> <Text style={styles.exercisetext}>{exercise.reps}</Text> </View> ); })} </View> ))} </ScreenContainer> ); }
额外优化说明
- 移除了冗余的
isFirstLoad状态,空依赖的useEffect会在组件挂载时自动执行一次getWorkouts。 - 所有临时数组变量(如
workoutsArray)都移到函数内部声明,避免每次渲染重新初始化。
内容的提问来源于stack exchange,提问作者kobo
相关产品推荐
相关产品推荐

