Redux 是 React 中用于全局状态管理的一个工具。它允许开发者在大型应用中集中管理应用的状态,使得状态的变化更加可预测和易于调试。Redux 的核心思想是将应用的所有状态存储在一个全局的 store 中,并通过严格定义的方式来修改这些状态。
Redux Store 的作用
- 集中管理状态:所有组件的状态都集中在一个 store 中,这样可以避免多个组件之间传递 props,简化组件间的通信。
- 可预测性:通过 reducer 函数来管理状态的变更,所有状态的变更都是通过 actions 明确的,保证了状态的变化是可预测和可追踪的。
- 调试友好:Redux 提供了强大的调试工具,比如 Redux DevTools,可以查看每一次状态的变化,回溯历史状态。
- 跨组件共享状态:即使两个没有直接关系的组件,也可以通过 store 来共享数据,而不必通过中间组件传递数据。
Redux 的核心概念
- Store:应用的单一状态树,所有状态都存储在这里。
- Action:描述状态变化的事件对象,通常包含一个
type字段和其他数据。 - Reducer:纯函数,根据当前的状态和 action 来生成新的状态。
- Dispatch:用于触发 action 的方法。
Redux 的工作流程
- 组件通过
dispatch方法发出一个 action。 - action 会被传递给 reducer 函数,reducer 根据当前的状态和传递进来的 action 来返回新的状态。
- store 根据 reducer 返回的状态更新,并通知所有订阅了 store 的组件。
代码示例
1. 创建 Redux Store
import { createStore } from 'redux';
// 定义初始状态
const initialState = {
count: 0,
};
// 定义 reducer 函数
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
// 创建 store
const store = createStore(counterReducer);
export default store;
或者直接派发
import { createStore } from 'redux';
// 定义初始状态
const initialState = {
count: 0,
};
// 定义 reducer 函数
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
// 创建 store
const store = createStore(counterReducer);
// 订阅 store 状态变化
store.subscribe(() => {
console.log('Store changed:', store.getState());
});
// 派发 actions
store.dispatch({ type: 'INCREMENT' }); // 输出: Store changed: { count: 1 }
store.dispatch({ type: 'DECREMENT' }); // 输出: Store changed: { count: 0 }
2. 组件使用 Redux Store
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
const Counter = () => {
// 读取 store 中的 count 状态
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<div>
<h1>{count}</h1>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>增加</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>减少</button>
</div>
);
};
export default Counter;
3. Provider 将 Store 注入组件树
在根组件中,通过 Provider 组件将 Redux store 注入到整个应用中。
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';
ReactDOM.render(
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
);
在 Redux 中,如果有多个 reducer 函数,通常会将它们合并成一个主 reducer,这样 Redux 可以根据 action.type 来判断应该由哪个 reducer 处理相应的 action。Redux 提供了 combineReducers 函数来合并多个 reducer。
每个子 reducer 只处理其负责的状态片段,而不是整个应用的状态树。Redux 会根据你定义的 key,将不同的 reducer 对应的状态片段合并到整个应用的状态树中。
多个 reducer 的管理:combineReducers
下面是一个使用多个 reducer 的示例,通过 combineReducers 将它们合并起来。
代码示例
1. 定义多个 reducer
import { combineReducers, createStore } from 'redux';
// 定义初始状态
const initialCounterState = { count: 0 };
const initialTodoState = { todos: [] };
// 定义第一个 reducer:管理计数器状态
const counterReducer = (state = initialCounterState, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
// 定义第二个 reducer:管理待办事项状态
const todoReducer = (state = initialTodoState, action) => {
switch (action.type) {
case 'ADD_TODO':
return { ...state, todos: [...state.todos, action.payload] };
case 'REMOVE_TODO':
return { ...state, todos: state.todos.filter((todo, index) => index !== action.index) };
default:
return state;
}
};
// 使用 combineReducers 将多个 reducer 合并
const rootReducer = combineReducers({
counter: counterReducer,
todos: todoReducer,
});
// 创建 store
const store = createStore(rootReducer);
在这里,我们定义了两个 reducer:
counterReducer处理计数器相关的状态。todoReducer处理待办事项列表。
然后通过 combineReducers 将它们合并到 rootReducer,并将 counterReducer 关联到 counter 状态,todoReducer 关联到 todos 状态。
2. 如何使用合并后的状态
当使用多个 reducer 后,状态树将会按照合并时定义的 key 来划分。例如,counter 对应 counterReducer 的状态片段,todos 对应 todoReducer 的状态片段。
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
const App = () => {
const count = useSelector((state) => state.counter.count); // 获取 counterReducer 中的 count 状态
const todos = useSelector((state) => state.todos.todos); // 获取 todoReducer 中的 todos 状态
const dispatch = useDispatch();
const addTodo = () => {
const newTodo = prompt("Enter a new todo:");
dispatch({ type: 'ADD_TODO', payload: newTodo });
};
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>增加</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>减少</button>
<h2>Todos:</h2>
<ul>
{todos.map((todo, index) => (
<li key={index}>{todo}</li>
))}
</ul>
<button onClick={addTodo}>添加待办事项</button>
</div>
);
};
export default App;
在这个组件中:
-
即使是不同的
Reducer,tpye也不能重复。 -
运行dispatch({ type: 'ADD_TODO', payload: newTodo } 或 dispatch({ type: 'DECREMENT' }) 时, Redux 会将这个 action 对象的 [type] 传递给所有的 reducer。这个 action 是一个包含
type字段的 JavaScript 对象,type字段告诉 reducer 需要进行什么样的状态变化。 -
useSelector((state) => state.counter.count)获取了counterReducer中管理的count状态。 -
useSelector((state) => state.todos.todos)获取了todoReducer中管理的todos列表。
combineReducers 的工作机制
combineReducers 的原理是将多个 reducer 合并为一个大的 reducer。当你 dispatch 一个 action 时,Redux 会将 action 传递给所有的子 reducer,并由每个子 reducer 决定是否对该 action 做出反应。最终的结果是各个子 reducer 返回的新状态片段被合并成整个状态树。
总结
- 通过
combineReducers可以将多个 reducer 合并,形成一个统一的rootReducer。 - 每个 reducer 负责管理自己状态树中的一部分。
- 通过
useSelector来选择特定状态片段,并通过dispatch不同的 action 来触发状态更新。