概述
Redux 适用场景:多交互、多数据源。
从组件角度看:某个组件的状态需要共享;某个状态需要在任何地方都可以拿到;一个组件需要改变全局状态或另一个组件的状态。
设计思想:
- Web 应用是一个状态机,视图与状态是一一对应的。
- 所有的状态,保存在一个对象里面。
三大原则
单一数据源 — 整个应用的 state 存储在唯一的 Store 中。
State 是只读的 — 唯一改变 state 的方法是触发 action,不能直接修改。
使用纯函数执行修改 — Reducer 是纯函数,接收旧 state 和 action,返回新 state。
核心概念
Store
保存数据的地方,整个应用只有一个。
import { createStore } from 'redux';
const store = createStore(reducer);
store.getState(); // 获取当前 state
store.dispatch(action); // 分发 action
store.subscribe(fn); // 注册监听器
Action
普通对象,描述”发生了什么”,必须包含 type 字段。
// Action Creator — 生成 Action 的函数
function addTodo(text) {
return { type: 'ADD_TODO', payload: { text, id: Date.now() } };
}
store.dispatch(addTodo('学习 Redux'));
Reducer
纯函数,接收当前 state 和 action,计算并返回新的 state。
function counterReducer(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
}
数据流
用户操作 → dispatch(action) → Reducer(state, action) → 新 state → 视图更新
完整示例:计数器
const { createStore } = require('redux');
// Action Types & Creators
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const RESET = 'RESET';
function increment() { return { type: INCREMENT }; }
function decrement() { return { type: DECREMENT }; }
function reset() { return { type: RESET }; }
// Reducer
function counterReducer(state = 0, action) {
switch (action.type) {
case INCREMENT: return state + 1;
case DECREMENT: return state - 1;
case RESET: return 0;
default: return state;
}
}
// 创建 Store 并使用
const store = createStore(counterReducer);
store.subscribe(() => console.log('计数:', store.getState()));
store.dispatch(increment()); // 计数: 1
store.dispatch(increment()); // 计数: 2
store.dispatch(decrement()); // 计数: 1
store.dispatch(reset()); // 计数: 0
完整示例:TodoList
const { createStore } = require('redux');
// Action Creators
function addTodo(text) {
return { type: 'ADD_TODO', payload: { text, id: Date.now() } };
}
function toggleTodo(id) {
return { type: 'TOGGLE_TODO', payload: { id } };
}
// Reducer
function todoReducer(state = { todos: [] }, action) {
switch (action.type) {
case 'ADD_TODO':
return { ...state, todos: [...state.todos, {
id: action.payload.id, text: action.payload.text, completed: false
}]};
case 'TOGGLE_TODO':
return { ...state, todos: state.todos.map(todo =>
todo.id === action.payload.id
? { ...todo, completed: !todo.completed } : todo
)};
default:
return state;
}
}
const store = createStore(todoReducer);
store.subscribe(() => {
const { todos } = store.getState();
console.log(todos.map(t => `${t.completed ? '✓' : '○'} ${t.text}`));
});
store.dispatch(addTodo('学习 Redux'));
store.dispatch(addTodo('写代码'));
Reducer 拆分与合并
import { combineReducers, createStore } from 'redux';
function userReducer(state = null, action) {
switch (action.type) {
case 'LOGIN': return action.payload;
case 'LOGOUT': return null;
default: return state;
}
}
function todosReducer(state = [], action) {
switch (action.type) {
case 'ADD_TODO': return [...state, action.payload];
default: return state;
}
}
const rootReducer = combineReducers({
user: userReducer,
todos: todosReducer
});
// state 结构: { user: null, todos: [] }
中间件 (Middleware)
中间件在 action 到达 reducer 之前进行拦截处理,常用于日志、异步操作等。
无中间件: dispatch(action) → Reducer → newState
有中间件: dispatch(action) → Middleware → ... → Reducer → newState
日志中间件
const logger = store => next => action => {
console.log('dispatching:', action.type);
const result = next(action);
console.log('next state:', store.getState());
return result;
};
异步中间件 (redux-thunk 原理)
// redux-thunk 核心实现
const thunk = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return next(action);
};
// 使用:异步 Action Creator
function fetchUser(userId) {
return async (dispatch) => {
dispatch({ type: 'FETCH_USER_START' });
try {
const res = await fetch(`/api/users/${userId}`);
const user = await res.json();
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
} catch (error) {
dispatch({ type: 'FETCH_USER_ERROR', payload: error.message });
}
};
}
应用中间件
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(rootReducer, applyMiddleware(logger, thunk));
与 React 配合
通过 react-redux 连接 Redux 和 React。
import { Provider, connect } from 'react-redux';
// 1. Provider 在顶层注入 Store
function App() {
return (
<Provider store={store}>
<TodoApp />
</Provider>
);
}
// 2. connect 连接组件
function TodoList({ todos, onToggle }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id} onClick={() => onToggle(todo.id)}>
{todo.text}
</li>
))}
</ul>
);
}
const mapStateToProps = state => ({ todos: state.todos });
const mapDispatchToProps = dispatch => ({
onToggle: id => dispatch(toggleTodo(id))
});
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
完整数据流
用户点击 → props 方法 → dispatch(action) → 中间件
→ Reducer → Store 更新 → mapStateToProps → 组件重新渲染
总结
Redux 核心思路:用 Store 存所有状态,用 Action 描述事件,用 Reducer 纯函数计算新状态。
适合 Redux:大量共享状态、复杂更新逻辑、需要状态追踪。不需要 Redux:简单 UI 状态、数据只在父子间传递、应用规模小。