说说你对 Redux 的理解?其工作原理?
问题解析
面试官考察点:
- 是否理解 Redux 的设计思想
- 是否掌握 Redux 的核心概念和工作流程
- 是否了解 Redux 中间件机制
- 对状态管理方案的理解
核心概念
什么是 Redux
Redux 是 JavaScript 应用的可预测状态容器,用于管理应用的全局状态。它基于 Flux 架构,采用单向数据流的设计思想。
三大核心原则
- 单一数据源:整个应用的 state 存储在一个对象树中
- State 是只读的:唯一改变 state 的方法是触发 action
- 使用纯函数执行修改:Reducer 是纯函数,接收旧 state 和 action,返回新 state
详细解答
1. 核心概念
Store
import { createStore } from 'redux';
// 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
const store = createStore(counterReducer);
// Store 的方法
console.log(store.getState()); // 获取当前 state
store.dispatch({ type: 'INCREMENT' }); // 派发 action
store.subscribe(() => { // 订阅 state 变化
console.log('State updated:', store.getState());
});
Action
// Action 是描述发生了什么的普通对象
const incrementAction = {
type: 'INCREMENT'
};
const addTodoAction = {
type: 'ADD_TODO',
payload: {
id: 1,
text: 'Learn Redux'
}
};
// Action Creator
function increment() {
return { type: 'INCREMENT' };
}
function addTodo(text) {
return {
type: 'ADD_TODO',
payload: { text }
};
}
// 使用
store.dispatch(increment());
store.dispatch(addTodo('Learn Redux'));
Reducer
// Reducer 是纯函数:(state, action) => newState
const initialState = {
todos: [],
filter: 'ALL'
};
function todoReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
case 'SET_FILTER':
return {
...state,
filter: action.payload
};
default:
return state;
}
}
// 合并多个 Reducer
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
todos: todoReducer,
user: userReducer,
settings: settingsReducer
});
2. 工作流程
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Action │ --> │ Store │ --> │ Reducer │
│ (dispatch) │ │ │ │ (纯函数) │
└─────────────┘ └─────────────┘ └──────┬──────┘
│
v
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ View │ <-- │ Subscribe │ <-- │ New State │
│ (React UI) │ │ (通知) │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
// 完整示例
import React from 'react';
import { createStore } from 'redux';
// 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
const store = createStore(counterReducer);
// React 组件
class Counter extends React.Component {
componentDidMount() {
// 订阅 state 变化
this.unsubscribe = store.subscribe(() => {
this.forceUpdate();
});
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
const state = store.getState();
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => store.dispatch({ type: 'INCREMENT' })}>
+
</button>
<button onClick={() => store.dispatch({ type: 'DECREMENT' })}>
-
</button>
</div>
);
}
}
3. 中间件(Middleware)
中间件扩展了 Redux,可以在 dispatch action 和到达 reducer 之间添加功能。
import { createStore, applyMiddleware } from 'redux';
// 自定义 Logger 中间件
const loggerMiddleware = store => next => action => {
console.log('dispatching:', action);
const result = next(action);
console.log('next state:', store.getState());
return result;
};
// 异步中间件(简化版)
const thunkMiddleware = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return next(action);
};
// 应用中间件
const store = createStore(
reducer,
applyMiddleware(loggerMiddleware, thunkMiddleware)
);
// 使用 thunk 进行异步操作
function fetchUser(userId) {
return async (dispatch, getState) => {
dispatch({ type: 'FETCH_USER_START' });
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_USER_ERROR', payload: error.message });
}
};
}
// 派发异步 action
store.dispatch(fetchUser(1));
4. React-Redux 集成
import React from 'react';
import { Provider, connect } from 'react-redux';
// 创建 Store
const store = createStore(rootReducer);
// 展示组件(Presentational Component)
function Counter({ count, increment, decrement }) {
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
// 容器组件(Container Component)
const mapStateToProps = (state) => ({
count: state.counter.count
});
const mapDispatchToProps = (dispatch) => ({
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' })
});
const ConnectedCounter = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);
// 应用根组件
function App() {
return (
<Provider store={store}>
<ConnectedCounter />
</Provider>
);
}
5. Redux Toolkit(现代 Redux)
import { createSlice, configureStore } from '@reduxjs/toolkit';
// 创建 Slice(包含 reducer 和 actions)
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1; // Redux Toolkit 使用 Immer,可以直接修改
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
// 自动生成的 action creators
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// 创建 Store
const store = configureStore({
reducer: {
counter: counterSlice.reducer
}
});
// 使用
store.dispatch(increment());
store.dispatch(incrementByAmount(5));
深入理解
Redux 数据流
┌─────────────────────────────────────────────────────────────┐
│ Action │
│ { type: 'ADD_TODO', payload: { id: 1, text: 'Learn' } } │
└───────────────────────┬─────────────────────────────────────┘
│ dispatch
v
┌─────────────────────────────────────────────────────────────┐
│ Middleware │
│ • Logger: 记录 action 和 state 变化 │
│ • Thunk: 处理异步操作 │
│ • Redux-Saga: 复杂异步流程 │
└───────────────────────┬─────────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────┐
│ Reducer │
│ (previousState, action) => newState │
│ • 纯函数,无副作用 │
│ • 不可变性:返回新对象 │
└───────────────────────┬─────────────────────────────────────┘
│
v
┌─────────────────────────────────────────────────────────────┐
│ Store │
│ • 单一数据源 │
│ • getState(): 获取当前 state │
│ • subscribe(): 订阅 state 变化 │
└───────────────────────┬─────────────────────────────────────┘
│ notify
v
┌─────────────────────────────────────────────────────────────┐
│ View │
│ • React 组件 │
│ • 通过 connect 或 hooks 连接 Redux │
└─────────────────────────────────────────────────────────────┘
不可变性(Immutability)
// 错误:直接修改 state
function badReducer(state, action) {
state.count = state.count + 1; // 直接修改!
return state;
}
// 正确:返回新对象
function goodReducer(state, action) {
return {
...state,
count: state.count + 1
};
}
// 使用展开运算符处理嵌套对象
function nestedReducer(state, action) {
return {
...state,
user: {
...state.user,
profile: {
...state.user.profile,
name: action.payload
}
}
};
}
// 或使用 Immutable.js / Immer
import produce from 'immer';
function immerReducer(state, action) {
return produce(state, draft => {
draft.user.profile.name = action.payload;
});
}
选择器(Selectors)
// 基础选择器
const getTodos = state => state.todos.items;
const getFilter = state => state.todos.filter;
// 派生选择器
const getVisibleTodos = createSelector(
[getTodos, getFilter],
(todos, filter) => {
switch (filter) {
case 'ACTIVE':
return todos.filter(t => !t.completed);
case 'COMPLETED':
return todos.filter(t => t.completed);
default:
return todos;
}
}
);
// 使用 reselect 进行记忆化
import { createSelector } from 'reselect';
const getTotalPrice = createSelector(
[state => state.cart.items],
(items) => items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
应用场景
1. 适合使用 Redux 的场景
- 应用有多个数据源需要共享
- 状态需要在多个组件间传递
- 复杂的状态逻辑
- 需要缓存或持久化状态
- 需要时间旅行调试
2. 不适合使用 Redux 的场景
- 简单的应用状态
- 只需要局部状态管理
- 学习成本考虑
// 使用 useState 替代 Redux
function SimpleApp() {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
<App />
</UserContext.Provider>
);
}
最佳实践
1. 项目结构
src/
├── store/
│ ├── index.js # Store 配置
│ └── middleware.js # 中间件配置
├── features/
│ ├── todos/
│ │ ├── todosSlice.js # Slice(reducer + actions)
│ │ ├── TodoList.jsx # 组件
│ │ └── todoAPI.js # API 调用
│ └── user/
│ ├── userSlice.js
│ └── UserProfile.jsx
└── components/ # 纯展示组件
2. Action 规范
// 使用 Flux Standard Action 规范
{
type: 'FETCH_USER_SUCCESS',
payload: { id: 1, name: 'John' },
meta: { timestamp: Date.now() },
error: false
}
// 错误处理
{
type: 'FETCH_USER_ERROR',
payload: new Error('Network Error'),
error: true
}
3. 使用 Redux Toolkit
// 推荐:使用 Redux Toolkit 简化代码
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// 创建异步 thunk
const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId, { rejectWithValue }) => {
try {
const response = await fetch(`/api/users/${userId}`);
return await response.json();
} catch (error) {
return rejectWithValue(error.message);
}
}
);
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false, error: null },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
});
}
});
面试要点
- 核心概念:Store、Action、Reducer
- 三大原则:单一数据源、State 只读、纯函数修改
- 工作流程:dispatch -> middleware -> reducer -> store -> view
- 中间件机制:扩展 Redux 功能,处理异步等
- React-Redux 集成:Provider、connect、hooks
常见追问:
- Redux 和 Context 有什么区别?
- 什么是 Redux 中间件?常用的有哪些?
- 如何处理 Redux 中的异步操作?
- 为什么 reducer 必须是纯函数?
- Redux Toolkit 解决了什么问题?