实现redux-thunk中间件
手写实现Redux的异步action处理中间件redux-thunk,支持dispatch函数类型的action
问题
Redux默认只支持dispatch普通对象类型的action,无法处理异步操作。redux-thunk是一个Redux中间件,它允许我们dispatch一个函数而不是普通对象,这个函数可以执行异步操作,并在适当的时候dispatch真正的action。
需要实现一个redux-thunk中间件,使得:
- 如果dispatch的action是函数,则执行该函数,并传入dispatch和getState
- 如果dispatch的action是普通对象,则正常传递给下一个中间件
解答
/**
* redux-thunk 中间件实现
* @param {Object} store - Redux store对象,包含dispatch、getState等方法
* @returns {Function} 返回中间件函数
*/
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// 如果action是函数,则执行该函数
// 并将dispatch、getState和额外参数传递给它
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 如果action不是函数,则传递给下一个中间件
return next(action);
};
}
// 默认的thunk中间件(不带额外参数)
const thunk = createThunkMiddleware();
// 支持自定义额外参数的thunk
thunk.withExtraArgument = createThunkMiddleware;
// 导出
export default thunk;
使用示例
import { createStore, applyMiddleware } from 'redux';
import thunk from './redux-thunk';
// 定义reducer
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
// 创建store并应用thunk中间件
const store = createStore(counterReducer, applyMiddleware(thunk));
// 普通的同步action creator
const increment = () => ({ type: 'INCREMENT' });
// 异步action creator(返回函数)
const incrementAsync = () => {
return (dispatch, getState) => {
setTimeout(() => {
console.log('当前状态:', getState());
dispatch(increment());
}, 1000);
};
};
// 带条件判断的异步action
const incrementIfOdd = () => {
return (dispatch, getState) => {
const { count } = getState();
if (count % 2 !== 0) {
dispatch(increment());
}
};
};
// 异步请求示例
const fetchUserData = (userId) => {
return async (dispatch, getState) => {
dispatch({ type: 'FETCH_USER_REQUEST' });
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
dispatch({ type: 'FETCH_USER_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_USER_FAILURE', error: error.message });
}
};
};
// 使用
store.dispatch(increment()); // 同步dispatch
store.dispatch(incrementAsync()); // 异步dispatch
store.dispatch(incrementIfOdd()); // 条件dispatch
store.dispatch(fetchUserData(123)); // 异步请求
// 使用额外参数的示例
const api = {
fetchUser: (id) => fetch(`/api/users/${id}`).then(r => r.json())
};
const thunkWithApi = thunk.withExtraArgument(api);
const store2 = createStore(counterReducer, applyMiddleware(thunkWithApi));
const loadUser = (userId) => {
return async (dispatch, getState, api) => {
const user = await api.fetchUser(userId);
dispatch({ type: 'USER_LOADED', payload: user });
};
};
store2.dispatch(loadUser(123));
关键点
-
中间件的柯里化结构:
store => next => action三层函数嵌套,符合Redux中间件规范- 第一层接收store对象(包含dispatch和getState)
- 第二层接收next函数(下一个中间件或原始dispatch)
- 第三层接收action并进行处理
-
类型判断:通过
typeof action === 'function'判断action是否为函数类型 -
函数执行:如果是函数类型,执行该函数并传入
dispatch和getState,使函数内部可以:- 访问当前state(通过getState)
- 派发新的action(通过dispatch)
- 执行异步操作
-
透传机制:如果不是函数类型,调用
next(action)将action传递给下一个中间件 -
返回值处理:函数类型的action执行结果会被返回,支持Promise等异步操作的链式调用
-
额外参数支持:通过
withExtraArgument方法可以注入自定义依赖(如API服务),增强可测试性
目录