在使用 React Redux 前,我们首先了解一下 Redux 的一些基础知识。
Redux 是 JavaScript 应用程序中用于状态管理的容器。它不依赖于任何框架,可以与任何 UI 库和框架一起使用。在应用程序中使用 Redux 时,Redux 是以可预测的方式管理状态。
Redux 的核心概念
- Store
应用系统中需要管理的数据,全部集中在这里
- Action
数据的处理都存放在这里,例如数据的添加、删除操作等
- Reducer
数据接收和转换存放在这里
Store、Action 和 Reducer 的三者关系
Redux 的使用原则
-
应用程序的状态存储形式是单个对象树
-
状态的获取必须通过
Action
来执行,在Action
中我们存放着对应很多动作的key
,我们只有执行对应的动作后才能拿到数据 -
拿到的数据必须通过
Reducer
来转换状态树的数据,而Reducer
在执行时会拿到Action
对应的动作,并接受返回的数据然后进行处理
Actions 说明
Action 的创建如下:
// 定义一个action
function buyCake() {
return {
type: BUY_CAKE,
info: "First redux action",
};
}
从上述的创建过程来看,action 其实是一个函数并且返回一个对象,其中返回的对象必须带有type
属性,这个属性在后续调用的时候会用到。
其次就是除了type
属性外,我们还可以根据自己的业务需求来扩展action
的属性。
Reducers 说明
Reducers 是专门负责接受状态并且执行对应的action
,并把执行完成后跟新对应的状态。
定义一个 Reducer 的过程很简单,具体代码如下:
// 定义一个 state
const initialState = {
numOfCakes: 20,
};
// 定义一个 reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case BUY_CAKE:
return {
numOfCakes: state.numOfCakes - 1,
};
default:
return state;
}
};
从上述的代码中可以看出Reducer
本质也是一个函数,并且接受两个函数。其中第一个参数是Store
,而第二个参数是action
,并且通过 type 来决定对状态执行什么操作。
Store 说明
Store 的职责主要包括如下几点:
- 负责保存应用程序状态
- 对外暴露
getState
的方法,通过此方法可以获取到应用程序中的状态值 - 提供了一个
dispatch(action)
的方法,主要是用于调度对应的action
,从而修改状态值 - 提供了一个注册监听器的方法
subscribe(listener)
,每当状态发生改变的时候都会触发此函数 - 提供了一个取消监听器的方法,用于收回已注册的监听器
实现Store
的过程如下:
const redux = require("redux");
const createStoreFn = redux.createStore; // 已经过时,这里只是展示概念性的东西,所以采用了旧方法
const store = createStoreFn(reducer); // 管理对应的状态
console.log(store.getState()); // 使用对外暴露获取状态数据的方法
// 注册监听器
const unsubscribe = store.subscribe(() =>
console.log("update state", store.getState())
);
// 调用对应的 action 方法
store.dispatch(buyCake());
store.dispatch(buyCake());
store.dispatch(buyCake());
unsubscribe();
如何实现多个 Reducers
只用一个 Reducers 来管理状态
function buyIceCream() {
return {
type: BUY_ICECREAM,
};
}
// 定义一个 State
const initialState = {
numOfCakes: 20,
numOfIceCream: 20,
};
// 定义一个 reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case BUY_CAKE:
return {
...state,
numOfCakes: state.numOfCakes - 1,
};
case BUY_ICECREAM:
return {
...state,
numOfIceCream: state.numOfIceCream - 1,
};
default:
return state;
}
};
通过上述的实例代码可以看出,我们只是简单的添加了一个action
而Reducer
就需要添加一个对应的状态处理方法,假如我们的业务逻辑比较复杂的话Reducer
则会变得非常难以维护,所以我们可以使用多个Reducer
来分解对状态处理的方法。
实现多个 Reducers
在实现多个 Reducers 的时候,我们需要用到combineReducers
来帮我们注册对应的Rducer
。具体的代码实例如下:
const initialCakeState = {
numOfCakes: 20,
};
const initialIceCreamState = {
numOfIceCream: 20,
};
const cakeReducer = (state = initialCakeState, action) => {
switch (action.type) {
case BUY_CAKE:
return {
...state,
numOfCakes: state.numOfCakes - 1,
};
default:
return state;
}
};
const iceCreamReducer = (state = initialIceCreamState, action) => {
switch (action.type) {
case BUY_ICECREAM:
return {
...state,
numOfIceCream: state.numOfIceCream - 1,
};
default:
return state;
}
};
const rootReducer = combineReducers({
cake: cakeReducer,
iceCream: iceCreamReducer,
});
const store = createStoreFn(rootReducer);
const unsubscribe = store.subscribe(() =>
console.log("update state", store.getState())
);
console.log(store.getState());
// 调用对应的 action 方法
store.dispatch(buyCake());
store.dispatch(buyCake());
store.dispatch(buyCake());
store.dispatch(buyIceCream());
store.dispatch(buyIceCream());
unsubscribe();
如何实现异步 Actions
上述的操作都是同步的,而接下来我们来实现一下异步的Actions
。
我们以获取用户列表数据为例一步一步实现异步 Actions。
- 定义
State
因为要实现异步的 Actions,所以我们的数据获取和处理会有一定处理时间,所以对应的State
属性应该包括如下几个属性:loading
、users
和error
。具体代码如下:
const initialState = {
loading: false, // 判断是否已经获取到数据
users: [],
error: "",
};
- 实现对应的 Actions
有了对应的状态数据,我们就可以编写一些对应的 Action 功能。具体的实例代码如下:
const FETCH_USERS_REQUEST = "FETCH_USERS_REQUEST";
const FETCH_USERS_SUCCESS = "FETCH_USERS_SUCCESS";
const FETCH_USERS_FAILURE = "FETCH_USERS_FAILURE";
const fetchUsersRequest = () => {
return {
type: FETCH_USERS_REQUEST,
};
};
const fetchUserSuccess = (users) => {
return {
type: FETCH_USERS_SUCCESS,
payload: users,
};
};
const fetchUserFaikuer = (error) => {
return {
type: FETCH_USERS_FAILURE,
payload: error,
};
};
- 实现对应的 Reducer
有了状态和执行动作,我们就可以实现对应的 Reducer。具体的代码如下:
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_USERS_REQUEST:
return {
...state,
lading: true,
};
case FETCH_USERS_SUCCESS:
return {
loading: false,
users: action.payload,
error: "",
};
case FETCH_USERS_FAILURE:
return {
loading: false,
users: [],
error: action.payload,
};
}
};
- 把
State
、Actions
和Reducer
建立关系
有了上述的准备,现在我们就可以把这三个元素建立关系。
在建立关系之前我们需要安装两个工具:
npm install axios redux-thunk
我们使用 redux 的中间件来实现异步的数据获取。具体代码如下:
const appluMiddleware = redux.applyMiddleware;
const thunkMiddleware = require("redux-thunk").default;
const axios = require("axios");
const store = createStoreFn(reducer, appluMiddleware(thunkMiddleware));
const fetchUsers = () => {
return function (dispatch) {
dispatch(fetchUsersRequest());
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
console.log(response);
const users = response.data.map((user) => user.id);
dispatch(fetchUserSuccess(users));
})
.catch((error) => {
dispatch(fetchUserFaikuer(error.message));
});
};
};
store.subscribe(() => {
console.log(store.getState());
});
store.dispatch(fetchUsers());