使用react-redux hooks时useSelector返回Promise、userEvents为undefined如何解决
问题根本原因
Redux要求reducer必须是无副作用的纯函数,不支持直接在reducer内部写异步逻辑。你给eventsReducer加了async关键字,导致reducer返回的是Promise对象,而非正常的state对象,这就是useSelector拿到Promise、访问userEvents报错的核心原因。
修复步骤
1. 重构reducer,移除异步逻辑
把reducer改成纯函数,只负责根据action更新state,删除async关键字和内部的firestore查询逻辑:
import { auth, firestore } from '../lib/firebase' const initialState = { events: [], userEvents: [] } export default function eventsReducer(state = initialState, action) { switch (action.type) { case 'events/set': return { ...state, events: action.payload } case 'userEvents/set': return { ...state, userEvents: action.payload } default: return state } }
2. 新增异步action处理数据拉取
用redux-thunk(redux默认支持的异步中间件)封装异步查询逻辑,编写对应的thunk函数:
// 异步拉取活动列表的thunk action export const loadEvents = () => async (dispatch) => { const userId = auth.currentUser.uid const eventsData = [] const userEventsData = [] // 两个查询并行执行,提升性能 const [queryEvents, userEvents] = await Promise.all([ firestore.collection("events").where("userId", "!=", userId).get(), firestore.collection("events").where("userId", "==", userId).get() ]) queryEvents.forEach(ev => { eventsData.push({ id: ev.id, ...ev.data() }) }) userEvents.forEach(ev => { userEventsData.push({ id: ev.id, ...ev.data() }) }) // 触发reducer更新state dispatch({ type: 'events/set', payload: eventsData }) dispatch({ type: 'userEvents/set', payload: userEventsData }) }
3. 修改App.js的dispatch逻辑
把原来直接dispatchevents/load的代码改成调用上面的thunk action:
// 原代码 await dispatch({ type: 'events/load' }) 替换为 await dispatch(loadEvents())
4. 修复Manage组件的逻辑
- 把
userEvents加到useEffect依赖数组,保证state更新后能重新计算活动列表 - 修复代码中未定义的
doc.id变量(应该用event.id)
const Manage = ({navigation}) => { const [loading, setLoading] = React.useState(true); const [events, setEvents] = React.useState([]); const eventsSelector = useSelector((state) => state.events) const { userEvents } = eventsSelector || {} useEffect(() => { if (!userEvents) return const storedEventsIds = new Set() // 用Set判断重复性能更好 const comingEvents = [] const passedEvents = [] userEvents.forEach(event => { if (storedEventsIds.has(event.id)) return const dateTimeStart = new Date(`${event.dateStart} ${event.timeStart}`) const dateTimeEnd = new Date(`${event.dateEnd} ${event.timeEnd}`) const dateNow = new Date() if (compareAsc(dateTimeStart, dateNow) >= 0 || (compareAsc(dateTimeStart, dateNow) < 0 && compareAsc(dateTimeEnd, dateNow) >= 0)) { comingEvents.push({...event, passed: false}) } else { passedEvents.push({...event, passed: true}) } storedEventsIds.add(event.id) }) setEvents([{ title: "Événements à venir", data: comingEvents }, { title: "Événéments passés", data: passedEvents }]); setLoading(false) }, [userEvents]) // 依赖userEvents,数据更新后自动重新计算 // 其余组件代码 }
优化建议
- 优先使用Redux Toolkit开发Redux逻辑,内置了thunk、immutable更新等能力,大幅减少样板代码,也能避免这类reducer异步的低级错误
- 可以把活动的已结束/未开始分类逻辑放到useSelector里计算,不用维护本地state,减少重复数据
- 建议给store添加loading、error状态,统一处理异步请求的加载和异常情况,不用每个组件单独维护加载状态
内容的提问来源于stack exchange,提问作者Bloodbee
相关产品推荐
相关产品推荐

