Redux Thunk调用异常:withFirebase包装后内部save函数未执行问题
问题分析与解决方案
首先咱们拆解下你遇到的核心问题:
为什么return withFirebase(save)不行,但return save能运行?
withFirebase是一个高阶组件(HOC),它的作用是给目标函数/组件注入firebase作为参数,但它返回的是一个被包装后的新函数/组件,而非直接执行你的save逻辑。- Redux Thunk中间件期望你在
dispatch => {...}里返回的是一个可直接调用的函数,但你返回的withFirebase(save)是一个等待接收props的HOC,Thunk不会自动去调用这个HOC,所以内部的save函数根本没机会执行。 - 当你改成
return save时,Thunk会直接调用这个save函数,但此时save的{firebase}参数其实是undefined——你觉得能正常运行,大概率是代码里存在全局firebase实例或隐藏的注入逻辑,但这显然不是规范的做法。
正确的解决方案(两种思路)
思路1:给Redux Thunk传入Firebase实例(推荐)
不需要在Thunk内部嵌套HOC,而是在创建Redux Store时,把Firebase实例通过thunk.withExtraArgument传进去,这样所有Thunk都能直接拿到Firebase:
- 配置Store时:
import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import firebase from '../../firebase'; // 你的Firebase实例 import rootReducer from './root-reducer'; const store = createStore( rootReducer, applyMiddleware(thunk.withExtraArgument({ firebase })) );
- 修改你的
saveUserThunk:
function saveUser() { // 第三个参数就是我们传入的额外参数,直接解构出firebase return (dispatch, getState, { firebase }) => { dispatch(saveUserStart()); const userRef = firebase.userDoc(firebase.auth.currentUser.uid); // 原有事务逻辑完全保留,直接使用firebase实例即可 firebase.db .runTransaction(transaction => { return transaction.get(userRef).then(doc => { if (!doc.exists) { return Promise.reject('Transaction failed: User dont exist!'); } const newRoles = doc.data().roles; newRoles.push(ROLES.USER); newRoles.splice(newRoles.indexOf('ANONYMOUS'), 1); transaction.update(userRef, { roles: newRoles }); return newRoles; }); }) .then(newRoles => { dispatch(saveUserSuccess(firebase.auth.currentUser)); console.log(`Transaction successfully committed role(s): ${newRoles}`); }) .catch(error => { dispatch(saveUserFailure(error)); console.log(`Transaction failed committed role(s): ${error}`); }); }; } export default saveUser;
思路2:用withFirebase包裹整个Thunk函数
如果你坚持要使用withFirebase,那应该把整个saveUser Thunk包裹在HOC里,而非在内部返回HOC:
import { userActionTypes } from './user.types'; import { withFirebase } from '../../firebase'; import * as ROLES from '../../constants/roles'; const saveUserStart = () => ({ type: userActionTypes.SAVE_USER_START, }); const saveUserSuccess = user => ({ type: userActionTypes.SAVE_USER_SUCCESS, payload: user, }); const saveUserFailure = errMsg => ({ type: userActionTypes.SAVE_USER_FAILURE, payload: errMsg, }); // 定义需要firebase的Thunk逻辑,参数里包含firebase function saveUser({ firebase }) { return dispatch => { dispatch(saveUserStart()); const userRef = firebase.userDoc(firebase.auth.currentUser.uid); // 事务逻辑不变 firebase.db .runTransaction(transaction => { return transaction.get(userRef).then(doc => { if (!doc.exists) { return Promise.reject('Transaction failed: User dont exist!'); } const newRoles = doc.data().roles; newRoles.push(ROLES.USER); newRoles.splice(newRoles.indexOf('ANONYMOUS'), 1); transaction.update(userRef, { roles: newRoles }); return newRoles; }); }) .then(newRoles => { dispatch(saveUserSuccess(firebase.auth.currentUser)); console.log(`Transaction successfully committed role(s): ${newRoles}`); }) .catch(error => { dispatch(saveUserFailure(error)); console.log(`Transaction failed committed role(s): ${error}`); }); }; } // 用withFirebase包裹整个saveUser,注入firebase参数 export default withFirebase(saveUser);
为什么React-Thunk会受withFirebase影响?
本质上不是Thunk受HOC影响,而是你混淆了两者的使用场景:
- HOC是React生态里用来给组件/函数注入props的工具,它的输出是一个需要被React渲染或手动传入props的函数。
- Redux Thunk是Redux中间件,它期望你返回的是一个能直接执行的函数(接收
dispatch、getState等参数)。
你之前的写法把HOC的返回值直接丢给了Thunk,但Thunk不知道如何处理这个HOC,自然不会执行你的save逻辑。
内容的提问来源于stack exchange,提问作者Kid
相关产品推荐
相关产品推荐

