Redux
依赖
redux react-redux
Redux 最终项目结构预览:
/src
/store
index.js # 创建并配置 store
reducers.js # 合并所有 reducer
actions.js # 集中定义所有 action creators (同步/异步)
/components
Counter.js
TodoList.js
App.js
reducer部分
第一步:定义同步和异步的 Action Creators
// 同步 Action Creators (返回一个 action 对象)
export const increment = (payload=1) => ({ type: 'COUNTER_INCREMENT',payload });
export const decrement = (payload=1) => ({ type: 'COUNTER_DECREMENT',payload });
export const setTodosLoading = () => ({ type: 'TODOS_LOADING' });
export const addTodo = (text) => ({ type: 'TODOS_ADD', payload: text });
// 异步 Action Creator (返回一个函数)
// thunk 中间件会发现 action 是一个函数,就会执行它,并传入 dispatch 和 getState 作为参数
export const addTodoAsync = (todoText) => {
return (dispatch) => {
dispatch(setTodosLoading());
setTimeout(() => {
dispatch(addTodo(`来自网络的待办: ${todoText}`));
}, 1000);
};
};
第二步:定义reducer,并使用 combineReducers管理复杂状态
// 计数器模块的 reducer
const counterReducer = (state = { count: 0 }, action) => {
switch (action.type) {
case "COUNTER_INCREMENT":
return { ...state, count: state.count + action.payload };
case "COUNTER_DECREMENT":
return { ...state, count: state.count - action.payload };
default:
return state;
}
};
// 待办事项模块的 reducer
const todosReducer = (state = { items: [], isLoading: false }, action) => {
switch (action.type) {
case "TODOS_LOADING":
return { ...state, isLoading: true };
case "TODOS_ADD":
return {
...state,
isLoading: false,
items: [...state.items, { id: Date.now(), text: action.payload }],
};
default:
return state;
}
};
// 使用 combineReducers 合并多个 reducer
import { combineReducers } from "redux";
const rootReducer = combineReducers({
counter: counterReducer,
todos: todosReducer,
});
export default rootReducer;
合并后,Redux Store 的状态树结构如下,非常清晰 :
{
counter: {
count: 0
},
todos: {
items: [],
isLoading: false
}
}
第三步:createStore创建 Store,redux-thunk处理异步
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';
import thunk from 'redux-thunk'; // 引入 thunk 中间件
// 使用 applyMiddleware 来增强 store,使其能处理函数类型的 action
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;
使用部分
计数器组件 (/src/components/Counter.js)
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from '../store/actions';
function Counter() {
const count = useSelector(state => state.counter.count);
const dispatch = useDispatch();
return (
<div>
<h2>计数器: {count}</h2>
<button onClick={() => dispatch(increment(1))}>+1</button>
<button onClick={() => dispatch(decrement(1))}>-1</button>
<button onClick={() => dispatch(increment(5))}>+5</button>
<button onClick={() => dispatch(decrement(5))}>-5</button>
</div>
);
}
export default Counter;
2. 待办列表组件 (/src/components/TodoList.js)
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addTodoAsync,addTodo } from "../store/actions";
function TodoList() {
const { items, isLoading } = useSelector((state) => state.todos);
const dispatch = useDispatch();
const [inputText, setInputText] = useState("");
return (
<div>
<h2>待办列表</h2>
<input
value={inputText}
onChange={(e) => setInputText(e.target.value)}
disabled={isLoading}
/>
<button
onClick={() => dispatch(addTodo(inputText))}
type="submit"
disabled={isLoading}
>
添加本地待办
</button>
<button
onClick={() => dispatch(addTodoAsync(inputText))}
type="submit"
disabled={isLoading}
>
{isLoading ? "添加中..." : "添加异步待办"}
</button>
<ul>
{items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</div>
);
}
export default TodoList;
3. 主应用组件 (/src/App.js)
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './components/Counter';
import TodoList from './components/TodoList';
function App() {
return (
<Provider store={store}>
<div className="App">
<h1>Redux最小实践</h1>
<Counter />
<TodoList />
</div>
</Provider>
);
}
export default App;
Redux Tookit 改写
依赖
@reduxjs/toolkit react-redux
Redux 最终项目结构预览:
/src
/store
/slice
counterSlice.js
todosSlice.js
index.js # 创建并配置 store
/components
Counter.js
TodoList.js
App.js
第一步: 创建 Counter Slice
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { count: 0 },
reducers: {
increment: (state,action) => {
state.count += action.payload;
},
decrement: (state,action) => {
state.count -= action.payload;
}
},
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
第一步: 创建 Todos Slice 含异步逻辑
createAsyncThunk 返回的是一个专门用于处理异步逻辑的 thunk action creator 函数,
这个函数被dispatch时会执行异步逻辑,会自动生成pending、fulfilled、rejected三种action类型
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
export const fetchTodoAsync = createAsyncThunk(
'todos/fetchTodo',
async (todoText) => {
const response = await new Promise(resolve =>
setTimeout(() => resolve(`来自网络的待办: ${todoText}`), 1000)
);
return response;
}
);
const todosSlice = createSlice({
name: 'todos',
initialState: {
items: [],
loading: false
},
reducers: {
addTodo: (state, action) => {
state.items.push(action.payload);
},
},
extraReducers: (builder) => {
builder
.addCase(fetchTodoAsync.pending, (state) => {
state.loading = true;
})
.addCase(fetchTodoAsync.fulfilled, (state, action) => {
state.loading = false;
state.items.push(action.payload);
})
.addCase(fetchTodoAsync.rejected, (state, action) => {
state.loading = false;
});
}
});
export const { addTodo } = todosSlice.actions;
export default todosSlice.reducer;
第三步:创建 store
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './slices/counterSlice';
import todosReducer from './slices/todosSlice';
const store = configureStore({
reducer: {
counter: counterReducer,
todos: todosReducer,
},
});
export default store
使用部分
计数器组件 (/src/components/Counter.js)
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement } from "../store/slices/counterSlice";
function Counter() {
const count = useSelector((state) => state.counter.count);
const dispatch = useDispatch();
return (
<div>
<h2>计数器: {count}</h2>
<button onClick={() => dispatch(increment(1))}>+1</button>
<button onClick={() => dispatch(decrement(1))}>-1</button>
<button onClick={() => dispatch(increment(5))}>+5</button>
<button onClick={() => dispatch(decrement(5))}>-5</button>
</div>
);
}
export default Counter;
2. 待办列表组件 (/src/components/TodoList.js)
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addTodo, fetchTodoAsync } from "../store/slices/todosSlice";
function TodoList() {
const { items, loading } = useSelector((state) => state.todos);
const dispatch = useDispatch();
const [inputText, setInputText] = useState("");
return (
<div>
<h2>待办列表</h2>
<input
value={inputText}
onChange={(e) => setInputText(e.target.value)}
disabled={loading}
/>
<button onClick={() => dispatch(addTodo(inputText))}>添加本地待办</button>
<button
onClick={() => dispatch(fetchTodoAsync(inputText))}
disabled={loading}
>
{loading ? "添加中..." : "添加异步待办"}
</button>
<ul>
{items.map((todo, index) => (
<li key={index}>{todo}</li>
))}
</ul>
</div>
);
}
export default TodoList;
3. 主应用组件 (/src/App.js)
import React from 'react';
import { Provider } from 'react-redux';
import store from './store/index';
import Counter from './components/Counter';
import TodoList from './components/TodoList';
function App() {
return (
<Provider store={store}>
<div className="App">
<h1>Redux最小实践</h1>
<Counter />
<TodoList />
</div>
</Provider>
);
}
export default App;
以下是用 Redux Toolkit (RTK) 对之前传统 Redux 实践进行现代化改造后的完整示例,对比效果显著:
🆚 新旧对比概览
| 特性 | 传统 Redux | Redux Toolkit (RTK) |
|---|---|---|
| Store 配置 | 手动 createStore+ applyMiddleware | configureStore 自动配置中间件 & DevTools |
| Reducer/Action | 手写 action types、action creators、switch-case | createSlice 自动生成 actions & reducers |
| 异步处理 | 需额外配置 redux-thunk, 需手动处理 pending/fulfilled/rejected状态 | 内置 createAsyncThunk 自动生成三种状态,extraReducers集中处理,逻辑更清晰 |
| 代码量 | 冗余(约 20-30 行/功能) | 简洁(约 5-10 行/功能) |