Appearance
Redux原理与中间件 - 状态管理深度解析
1. Redux核心概念
1.1 三大原则
typescript
const reduxPrinciples = {
单一数据源: {
原则: '整个应用的state存储在单一store中',
好处: ['易于调试', '易于持久化', '易于同构'],
示例: `
const store = {
user: { id: 1, name: 'John' },
todos: [{ id: 1, text: 'Learn Redux' }],
ui: { theme: 'dark' }
};
`
},
State只读: {
原则: 'State只能通过dispatch action来修改',
好处: ['可预测', '易于追踪', '时间旅行调试'],
示例: `
// ❌ 错误
store.state.user.name = 'Jane';
// ✓ 正确
store.dispatch({ type: 'UPDATE_USER', payload: { name: 'Jane' } });
`
},
纯函数修改: {
原则: '使用纯函数reducer来描述state变化',
好处: ['可测试', '可预测', '易于理解'],
示例: `
function userReducer(state = initialState, action) {
switch (action.type) {
case 'UPDATE_USER':
return { ...state, ...action.payload };
default:
return state;
}
}
`
}
};1.2 核心API
typescript
const coreAPIs = {
createStore: {
作用: '创建Redux store',
签名: 'createStore(reducer, preloadedState, enhancer)',
示例: `
import { createStore } from 'redux';
const store = createStore(rootReducer, initialState);
`
},
combineReducers: {
作用: '合并多个reducer',
示例: `
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
user: userReducer,
todos: todosReducer,
ui: uiReducer
});
`
},
applyMiddleware: {
作用: '应用中间件',
示例: `
import { applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(
rootReducer,
applyMiddleware(thunk, logger)
);
`
},
bindActionCreators: {
作用: '绑定action creators到dispatch',
示例: `
const boundActions = bindActionCreators(
{ increment, decrement },
store.dispatch
);
`
}
};2. Redux实现原理
2.1 createStore实现
typescript
/**
* 简化版createStore
*/
function createStore(reducer, preloadedState, enhancer) {
// 处理enhancer
if (typeof enhancer !== 'undefined') {
return enhancer(createStore)(reducer, preloadedState);
}
let currentReducer = reducer;
let currentState = preloadedState;
let currentListeners = [];
let nextListeners = currentListeners;
let isDispatching = false;
// 确保可以修改listeners
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
}
// 获取当前state
function getState() {
if (isDispatching) {
throw new Error('不能在reducer执行时调用getState');
}
return currentState;
}
// 订阅state变化
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('listener必须是函数');
}
if (isDispatching) {
throw new Error('不能在reducer执行时订阅');
}
let isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
// 返回取消订阅函数
return function unsubscribe() {
if (!isSubscribed) {
return;
}
if (isDispatching) {
throw new Error('不能在reducer执行时取消订阅');
}
isSubscribed = false;
ensureCanMutateNextListeners();
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
currentListeners = null;
};
}
// 派发action
function dispatch(action) {
if (typeof action.type === 'undefined') {
throw new Error('action必须有type属性');
}
if (isDispatching) {
throw new Error('Reducer不能dispatch action');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
// 通知所有监听器
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
return action;
}
// 替换reducer (用于代码分割)
function replaceReducer(nextReducer) {
currentReducer = nextReducer;
dispatch({ type: '@@redux/REPLACE' });
return store;
}
// 初始化state
dispatch({ type: '@@redux/INIT' });
return {
dispatch,
subscribe,
getState,
replaceReducer
};
}2.2 combineReducers实现
typescript
/**
* 简化版combineReducers
*/
function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
const finalReducers = {};
// 过滤有效的reducer
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key];
}
}
const finalReducerKeys = Object.keys(finalReducers);
// 返回组合后的reducer
return function combination(state = {}, action) {
let hasChanged = false;
const nextState = {};
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i];
const reducer = finalReducers[key];
const previousStateForKey = state[key];
const nextStateForKey = reducer(previousStateForKey, action);
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;
return hasChanged ? nextState : state;
};
}
// 使用示例
const rootReducer = combineReducers({
user: (state = null, action) => {
if (action.type === 'SET_USER') return action.payload;
return state;
},
todos: (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
case 'REMOVE_TODO':
return state.filter(t => t.id !== action.payload);
default:
return state;
}
}
});
// 等价于
function rootReducer(state = {}, action) {
return {
user: userReducer(state.user, action),
todos: todosReducer(state.todos, action)
};
}3. 中间件原理
3.1 中间件概念
typescript
const middlewareConcept = {
定义: '在dispatch和reducer之间的扩展点',
作用: [
'处理异步操作',
'日志记录',
'错误报告',
'路由跳转',
'调用API'
],
签名: `
const middleware = store => next => action => {
// 中间件逻辑
return next(action);
};
`,
执行流程: `
dispatch(action)
-> middleware1
-> middleware2
-> middleware3
-> reducer
-> 更新state
-> 通知订阅者
`
};3.2 applyMiddleware实现
typescript
/**
* 简化版applyMiddleware
*/
function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState) => {
// 创建store
const store = createStore(reducer, preloadedState);
let dispatch = () => {
throw new Error('Dispatching while constructing middleware');
};
// 传递给中间件的API
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
};
// 应用所有中间件
const chain = middlewares.map(middleware => middleware(middlewareAPI));
// 组合中间件
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
};
}
/**
* compose函数
*/
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
// 示例
const middleware1 = store => next => action => {
console.log('middleware1 before');
const result = next(action);
console.log('middleware1 after');
return result;
};
const middleware2 = store => next => action => {
console.log('middleware2 before');
const result = next(action);
console.log('middleware2 after');
return result;
};
const store = createStore(
reducer,
applyMiddleware(middleware1, middleware2)
);
store.dispatch({ type: 'TEST' });
// 输出:
// middleware1 before
// middleware2 before
// reducer执行
// middleware2 after
// middleware1 after3.3 常用中间件
typescript
// Logger中间件
const logger = store => next => action => {
console.group(action.type);
console.log('dispatching', action);
console.log('prev state', store.getState());
const result = next(action);
console.log('next state', store.getState());
console.groupEnd();
return result;
};
// Thunk中间件
const thunk = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return next(action);
};
// 使用thunk
const fetchUser = (id) => {
return async (dispatch, getState) => {
dispatch({ type: 'FETCH_USER_START' });
try {
const user = await api.fetchUser(id);
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_USER_ERROR', error });
}
};
};
// Promise中间件
const promiseMiddleware = store => next => action => {
if (action.payload && typeof action.payload.then === 'function') {
action.payload
.then(result => {
store.dispatch({ ...action, payload: result });
})
.catch(error => {
store.dispatch({ ...action, payload: error, error: true });
});
return;
}
return next(action);
};4. React-Redux集成
4.1 Provider实现
typescript
/**
* 简化版Provider
*/
const ReactReduxContext = React.createContext(null);
function Provider({ store, children }) {
const contextValue = useMemo(() => ({ store }), [store]);
return (
<ReactReduxContext.Provider value={contextValue}>
{children}
</ReactReduxContext.Provider>
);
}
// 使用
<Provider store={store}>
<App />
</Provider>4.2 useSelector实现
typescript
/**
* 简化版useSelector
*/
function useSelector(selector, equalityFn = Object.is) {
const { store } = useContext(ReactReduxContext);
const [, forceRender] = useReducer(s => s + 1, 0);
const selectedState = useRef(null);
const selectorRef = useRef(selector);
const equalityFnRef = useRef(equalityFn);
// 更新refs
selectorRef.current = selector;
equalityFnRef.current = equalityFn;
// 计算选中的state
const newSelectedState = selectorRef.current(store.getState());
useEffect(() => {
selectedState.current = newSelectedState;
});
useEffect(() => {
function checkForUpdates() {
const newSelectedState = selectorRef.current(store.getState());
if (equalityFnRef.current(selectedState.current, newSelectedState)) {
return;
}
selectedState.current = newSelectedState;
forceRender();
}
const unsubscribe = store.subscribe(checkForUpdates);
checkForUpdates();
return unsubscribe;
}, [store]);
return newSelectedState;
}
// 使用
function Component() {
const user = useSelector(state => state.user);
const todos = useSelector(state => state.todos);
return <div>{user.name}</div>;
}4.3 useDispatch实现
typescript
/**
* 简化版useDispatch
*/
function useDispatch() {
const { store } = useContext(ReactReduxContext);
return store.dispatch;
}
// 使用
function Component() {
const dispatch = useDispatch();
const handleClick = () => {
dispatch({ type: 'INCREMENT' });
};
return <button onClick={handleClick}>Increment</button>;
}5. 面试高频问题
typescript
const interviewQA = {
Q1: {
question: 'Redux的工作流程?',
answer: [
'1. 组件dispatch action',
'2. action通过middleware',
'3. reducer处理action',
'4. 生成新state',
'5. 通知订阅者',
'6. 组件re-render'
]
},
Q2: {
question: 'Redux中间件原理?',
answer: `
中间件洋葱模型:
- 柯里化函数
- store => next => action
- compose组合多个中间件
- 形成调用链
`
},
Q3: {
question: 'Redux vs Context?',
answer: [
'Redux: 可预测、时间旅行、中间件',
'Context: 简单、内置、无额外依赖',
'Redux适合: 复杂状态、大型应用',
'Context适合: 简单状态、主题配置'
]
}
};6. 总结
Redux原理的核心要点:
- 三大原则: 单一数据源、只读、纯函数
- 核心API: createStore、combineReducers
- 中间件: 洋葱模型、异步处理
- React集成: Provider、useSelector、useDispatch
- 优化: 精确订阅、memo、reselect
掌握Redux是理解状态管理的基础。
7. Redux源码深入分析
7.1 createStore完整实现
javascript
// Redux createStore 完整实现
function createStore(reducer, preloadedState, enhancer) {
// 处理参数重载
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState;
preloadedState = undefined;
}
// 如果有enhancer(中间件),先应用
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected enhancer to be a function');
}
return enhancer(createStore)(reducer, preloadedState);
}
// 内部状态
let currentReducer = reducer;
let currentState = preloadedState;
let currentListeners = [];
let nextListeners = currentListeners;
let isDispatching = false;
// 确保可以修改listeners而不影响当前迭代
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice();
}
}
// 获取当前状态
function getState() {
if (isDispatching) {
throw new Error('不能在reducer执行时调用getState');
}
return currentState;
}
// 订阅状态变化
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function');
}
if (isDispatching) {
throw new Error('不能在reducer执行时调用subscribe');
}
let isSubscribed = true;
ensureCanMutateNextListeners();
nextListeners.push(listener);
// 返回取消订阅函数
return function unsubscribe() {
if (!isSubscribed) {
return;
}
if (isDispatching) {
throw new Error('不能在reducer执行时调用unsubscribe');
}
isSubscribed = false;
ensureCanMutateNextListeners();
const index = nextListeners.indexOf(listener);
nextListeners.splice(index, 1);
currentListeners = null;
};
}
// 派发action
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error('Action必须是plain object');
}
if (typeof action.type === 'undefined') {
throw new Error('Action必须有type属性');
}
if (isDispatching) {
throw new Error('Reducer不能dispatch action');
}
try {
isDispatching = true;
currentState = currentReducer(currentState, action);
} finally {
isDispatching = false;
}
// 通知所有订阅者
const listeners = (currentListeners = nextListeners);
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i];
listener();
}
return action;
}
// 替换reducer(热重载使用)
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected nextReducer to be a function');
}
currentReducer = nextReducer;
dispatch({ type: '@@redux/REPLACE' });
}
// 初始化状态
dispatch({ type: '@@redux/INIT' });
return {
dispatch,
subscribe,
getState,
replaceReducer
};
}
// 工具函数
function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false;
let proto = obj;
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(obj) === proto;
}7.2 combineReducers原理
javascript
// combineReducers 完整实现
function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
const finalReducers = {};
// 过滤掉非函数的reducer
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key];
}
}
const finalReducerKeys = Object.keys(finalReducers);
// 返回组合后的reducer
return function combination(state = {}, action) {
let hasChanged = false;
const nextState = {};
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i];
const reducer = finalReducers[key];
const previousStateForKey = state[key];
const nextStateForKey = reducer(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') {
throw new Error(`Reducer "${key}" 返回了 undefined`);
}
nextState[key] = nextStateForKey;
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;
return hasChanged ? nextState : state;
};
}
// 使用示例
const rootReducer = combineReducers({
user: userReducer,
todos: todosReducer,
posts: postsReducer
});
// 等价于
function rootReducer(state = {}, action) {
return {
user: userReducer(state.user, action),
todos: todosReducer(state.todos, action),
posts: postsReducer(state.posts, action)
};
}7.3 applyMiddleware源码
javascript
// applyMiddleware 完整实现
function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState) => {
const store = createStore(reducer, preloadedState);
let dispatch = store.dispatch;
let chain = [];
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
};
// 给每个中间件注入 dispatch 和 getState
chain = middlewares.map(middleware => middleware(middlewareAPI));
// 组合中间件链
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
};
};
}
// compose 函数实现
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
// 中间件执行流程示例
const middleware1 = store => next => action => {
console.log('middleware1: before');
const result = next(action);
console.log('middleware1: after');
return result;
};
const middleware2 = store => next => action => {
console.log('middleware2: before');
const result = next(action);
console.log('middleware2: after');
return result;
};
const middleware3 = store => next => action => {
console.log('middleware3: before');
const result = next(action);
console.log('middleware3: after');
return result;
};
// 执行顺序:
// middleware1: before
// middleware2: before
// middleware3: before
// [reducer执行]
// middleware3: after
// middleware2: after
// middleware1: after8. 高级中间件实现
8.1 异步中间件thunk源码
javascript
// redux-thunk 完整实现
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// 如果action是函数,执行它
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 否则传递给下一个中间件
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
// 使用示例
function fetchUser(userId) {
return async (dispatch, getState) => {
dispatch({ type: 'FETCH_USER_REQUEST' });
try {
const response = await api.fetchUser(userId);
dispatch({
type: 'FETCH_USER_SUCCESS',
payload: response.data
});
} catch (error) {
dispatch({
type: 'FETCH_USER_FAILURE',
payload: error.message
});
}
};
}8.2 Promise中间件实现
javascript
// redux-promise 实现
const promiseMiddleware = ({ dispatch }) => next => action => {
// 如果action.payload是Promise
if (action.payload && typeof action.payload.then === 'function') {
return action.payload
.then(result => dispatch({ ...action, payload: result }))
.catch(error => dispatch({ ...action, payload: error, error: true }));
}
return next(action);
};
// 使用示例
dispatch({
type: 'FETCH_USER',
payload: api.fetchUser(userId) // Promise
});8.3 Logger中间件实现
javascript
// redux-logger 简化实现
const logger = store => next => action => {
console.group(action.type);
console.log('prev state', store.getState());
console.log('action', action);
const result = next(action);
console.log('next state', store.getState());
console.groupEnd();
return result;
};
// 增强版logger
const createLogger = (options = {}) => {
const {
collapsed = true,
duration = true,
timestamp = true,
colors = {
title: () => 'inherit',
prevState: () => '#9E9E9E',
action: () => '#03A9F4',
nextState: () => '#4CAF50',
error: () => '#F20404'
}
} = options;
return store => next => action => {
const startTime = Date.now();
const prevState = store.getState();
const formattedTime = timestamp
? new Date().toLocaleTimeString()
: '';
const title = `action ${action.type} ${formattedTime}`;
const groupMethod = collapsed ? console.groupCollapsed : console.group;
try {
groupMethod(`%c ${title}`, `color: ${colors.title()}`);
} catch (e) {
console.log(title);
}
console.log('%c prev state', `color: ${colors.prevState()}`, prevState);
console.log('%c action', `color: ${colors.action()}`, action);
const result = next(action);
const nextState = store.getState();
const endTime = Date.now();
console.log('%c next state', `color: ${colors.nextState()}`, nextState);
if (duration) {
console.log(`%c duration`, 'color: #9E9E9E', `${endTime - startTime}ms`);
}
try {
console.groupEnd();
} catch (e) {
console.log('— log end —');
}
return result;
};
};8.4 错误处理中间件
javascript
// 错误捕获中间件
const crashReporter = store => next => action => {
try {
return next(action);
} catch (err) {
console.error('捕获一个异常!', err);
// 发送错误报告到服务器
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
});
throw err;
}
};
// 异步错误处理中间件
const errorHandlerMiddleware = store => next => action => {
const result = next(action);
// 处理Promise rejection
if (result && typeof result.catch === 'function') {
return result.catch(error => {
console.error('异步操作失败:', error);
// 分发错误action
store.dispatch({
type: 'GLOBAL_ERROR',
payload: error,
error: true
});
return Promise.reject(error);
});
}
return result;
};9. Redux Toolkit深入
9.1 createSlice源码分析
javascript
// createSlice 简化实现
function createSlice(options) {
const {
name,
initialState,
reducers = {},
extraReducers
} = options;
const actionCreators = {};
const sliceReducers = {};
// 生成action creators和reducers
Object.keys(reducers).forEach(reducerName => {
const type = `${name}/${reducerName}`;
// Action creator
actionCreators[reducerName] = (payload) => ({
type,
payload
});
// Reducer
sliceReducers[type] = reducers[reducerName];
});
// 主reducer
function reducer(state = initialState, action) {
// 处理slice自己的actions
if (sliceReducers[action.type]) {
return sliceReducers[action.type](state, action);
}
// 处理extraReducers
if (extraReducers && extraReducers[action.type]) {
return extraReducers[action.type](state, action);
}
return state;
}
return {
name,
reducer,
actions: actionCreators
};
}
// 使用示例
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;9.2 createAsyncThunk实现
javascript
// createAsyncThunk 简化实现
function createAsyncThunk(typePrefix, payloadCreator, options = {}) {
const pending = `${typePrefix}/pending`;
const fulfilled = `${typePrefix}/fulfilled`;
const rejected = `${typePrefix}/rejected`;
// Action creators
const actionCreators = {
pending: (requestId, arg) => ({
type: pending,
meta: { requestId, arg }
}),
fulfilled: (payload, requestId, arg) => ({
type: fulfilled,
payload,
meta: { requestId, arg }
}),
rejected: (error, requestId, arg) => ({
type: rejected,
payload: error,
error: true,
meta: { requestId, arg }
})
};
// Thunk action creator
function thunk(arg) {
return async (dispatch, getState, extra) => {
const requestId = nanoid();
dispatch(actionCreators.pending(requestId, arg));
try {
const result = await payloadCreator(arg, {
dispatch,
getState,
extra,
requestId
});
dispatch(actionCreators.fulfilled(result, requestId, arg));
return result;
} catch (error) {
dispatch(actionCreators.rejected(error, requestId, arg));
throw error;
}
};
}
// 添加类型属性
thunk.pending = pending;
thunk.fulfilled = fulfilled;
thunk.rejected = rejected;
return thunk;
}
// 使用示例
const fetchUser = createAsyncThunk(
'users/fetch',
async (userId, { getState }) => {
const response = await api.fetchUser(userId);
return response.data;
}
);
// 在slice中处理
const usersSlice = createSlice({
name: 'users',
initialState: {
entities: {},
loading: 'idle',
error: null
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = 'pending';
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = 'idle';
state.entities[action.payload.id] = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = 'idle';
state.error = action.payload;
});
}
});10. 性能优化深入
10.1 selector性能优化
javascript
// reselect 实现原理
function createSelector(...funcs) {
const resultFunc = funcs.pop();
const dependencies = funcs;
let lastArgs = null;
let lastResult = null;
return function selector(state, props) {
const args = dependencies.map(dep => dep(state, props));
// 浅比较参数是否变化
if (lastArgs === null || !shallowEqual(args, lastArgs)) {
lastResult = resultFunc(...args);
lastArgs = args;
}
return lastResult;
};
}
function shallowEqual(a, b) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
// 高级selector模式
import { createSelector, createStructuredSelector } from 'reselect';
// 基础selectors
const selectUser = state => state.user;
const selectPosts = state => state.posts;
const selectFilter = (state, props) => props.filter;
// 派生selectors
const selectUserPosts = createSelector(
[selectPosts, selectUser],
(posts, user) => posts.filter(post => post.authorId === user.id)
);
const selectFilteredPosts = createSelector(
[selectUserPosts, selectFilter],
(posts, filter) => {
switch (filter) {
case 'published':
return posts.filter(p => p.published);
case 'draft':
return posts.filter(p => !p.published);
default:
return posts;
}
}
);
// 结构化selector
const mapState = createStructuredSelector({
user: selectUser,
posts: selectFilteredPosts,
postsCount: state => selectFilteredPosts(state).length
});10.2 Redux批量更新
javascript
// 批量dispatch
import { batch } from 'react-redux';
function updateMultipleStates() {
batch(() => {
dispatch(action1());
dispatch(action2());
dispatch(action3());
});
// 只触发一次重渲染
}
// 批量更新中间件
const batchMiddleware = store => {
let batchedActions = [];
let isBatching = false;
return next => action => {
if (action.type === 'BATCH_START') {
isBatching = true;
return;
}
if (action.type === 'BATCH_END') {
isBatching = false;
batch(() => {
batchedActions.forEach(a => next(a));
});
batchedActions = [];
return;
}
if (isBatching) {
batchedActions.push(action);
return;
}
return next(action);
};
};11. Redux面试重点
11.1 高频面试题
typescript
const interviewQuestions = [
{
q: 'Redux三大原则是什么?',
a: `
1. 单一数据源:整个应用的state存储在一个store中
2. State是只读的:只能通过dispatch action来修改
3. 使用纯函数执行修改:reducer必须是纯函数
`
},
{
q: 'Redux中间件原理是什么?',
a: `
洋葱模型:
1. 中间件接收store的dispatch和getState
2. 返回一个函数接收next
3. 再返回一个函数接收action
4. 可以在调用next前后执行逻辑
5. compose函数组合所有中间件
`
},
{
q: 'Redux如何处理异步?',
a: `
通过中间件:
1. redux-thunk:action可以是函数
2. redux-saga:使用Generator管理副作用
3. redux-observable:使用RxJS
4. RTK Query:内置数据获取
`
},
{
q: 'useSelector性能如何优化?',
a: `
1. 使用reselect创建memoized selector
2. 使用shallowEqual比较函数
3. 拆分selector,避免返回新对象
4. 使用createStructuredSelector
5. 考虑使用normalizedState
`
},
{
q: 'Redux vs Context,何时用Redux?',
a: `
使用Redux场景:
1. 需要强大的调试工具
2. 需要时间旅行调试
3. 需要中间件处理副作用
4. 需要持久化状态
5. 大型应用,复杂状态逻辑
`
}
];11.2 手写题精选
javascript
// 1. 手写mini Redux
class MiniRedux {
constructor(reducer, initialState) {
this.reducer = reducer;
this.state = initialState;
this.listeners = [];
}
getState() {
return this.state;
}
dispatch(action) {
this.state = this.reducer(this.state, action);
this.listeners.forEach(listener => listener());
}
subscribe(listener) {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
}
// 2. 手写combineReducers
function myCombineReducers(reducers) {
return (state = {}, action) => {
return Object.keys(reducers).reduce((nextState, key) => {
nextState[key] = reducers[key](state[key], action);
return nextState;
}, {});
};
}
// 3. 手写applyMiddleware
function myApplyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
const store = createStore(reducer);
const chain = middlewares.map(middleware =>
middleware(store)
);
const dispatch = compose(...chain)(store.dispatch);
return { ...store, dispatch };
};
}