持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
简介
Redux
的使用一直是很多人在React
开发中无法逃开的痛,各种中间件,各种配置,各种目录规范,让很多想尝试的人选择了放弃。不过Redux
官方推出了Redux Toolkit
这个库,肯定符合大家的胃口,可谓的完美验证了真香定律。借这次机会就简单的给大家普及一下这个库的使用。
我们知道在React
中使用Redux
需要借助React-Redux
,React-Redux
我们知道,是用来连接React
和Redux
的桥梁,但是Redux Toolkit
又是干什么的呢?
Redux Toolkit
简答总结就是一个Redux
工具包,用来简化Redux
操作的。
本文分为两部份,第一部分介绍传统的Redux、React-Redux
的使用方式。第二部分我们引入Redux Toolkit
,看看Redux Toolkit
到底是怎样来简化我们Redux、React-Redux
的使用。
React-Redux
我们平时使用React-Redux
的时候一般分为以下七步。
- 安装
- 创建types
- 创建actions
- 创建reducers
- 创建store
- 将Redux连接到React
- 在React中使用
为方便理解,笔者例子文件总体目录结构如下:
store
├── actions
│ ├── counterActions.js
│ └── userSlice.js
├── reducers
│ ├── counterReducer.js
│ └── userReducer.js
├── types
└── index.js
复制代码
安装
首先我们需要安装redux react-redux
两个包。
npm i redux react-redux
复制代码
创建types
然后我们需要创建需要用到的types
,并且需要保持每个type
唯一性。
export const counterIncrementType = "counter/incremented";
export const counterDecrementType = "counter/decremented";
export const userNameIncrementType = "userName/incremented";
export const userNameDecrementType = "userName/decremented";
export const userAgeIncrementType = "userAge/incremented";
export const userAgeDecrementType = "userAge/decremented";
复制代码
创建action
然后我们需要创建各模块的action
counterActions

import { counterIncrementType, counterDecrementType } from "../types";
export const counterIncrementAction = () => {
return {
type: counterIncrementType,
};
};
export const counterDecrementAction = () => {
return {
type: counterDecrementType,
};
};
复制代码
userActions
import {
userNameIncrementType,
userNameDecrementType,
userAgeIncrementType,
userAgeDecrementType,
} from "../types";
export const userNameIncrementAction = () => {
return {
type: userNameIncrementType,
};
};
export const userNameDecrementAction = () => {
return {
type: userNameDecrementType,
};
};
export const userAgeIncrementAction = (payload) => {
return {
type: userAgeIncrementType,
payload,
};
};
export const userAgeDecrementAction = (payload) => {
return {
type: userAgeDecrementType,
payload,
};
};
复制代码
创建 Reducer
然后需要创建我们处理state
的reducer
,这里需要注意,不是直接修改state
,而是每次返回的新的state
。
一般在项目中,为了保证每次返回的state
都是全新的一般会搭配immutable-js和redux-immutable来使用。 counterReducer
import { counterIncrementType, counterDecrementType } from "../types";
export default function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case counterIncrementType:
return { value: state.value + 1 };
case counterDecrementType:
return { value: state.value - 1 };
default:
return state;
}
}
复制代码
userReducer
import {
userNameIncrementType,
userNameDecrementType,
userAgeIncrementType,
userAgeDecrementType,
} from "../types";
export default function userReducer(
state = { name: "randy", age: 24 },
action
) {
switch (action.type) {
case userNameIncrementType:
return { ...state, name: state.name + "!" };
case userNameDecrementType:
return { ...state, name: state.name.slice(0, state.name.length - 1) };
case userAgeIncrementType:
return { ...state, age: state.age + action.payload };
case userAgeDecrementType:
return { ...state, age: -action.payload };
default:
return state;
}
}
复制代码
合并Reducer
当我们系统庞大后就不可能只使用一个reducer
,使用需要使用多个reducer
。
多个reducer
使用combineReducers
合并起来。
const redicers = combineReducers({
user: userReducer,
counter: counterReducer,
});
复制代码
当然如果我们只有单个reducer
的话,直接传递给combineReducers
就可以了。
const redicers = combineReducers(reducer);
复制代码
创建store
传递redicers
来创建store
。
这里我们如果想用chrome
插件来查看我们的Redux
的话必须结合redux-devtools-extension来使用。
import { createStore, combineReducers } from "redux";
import counterReducer from "./reducers/counterReducer";
import userReducer from "./reducers/userReducer";
const redicers = combineReducers({
counter: counterReducer,
user: userReducer,
});
export default createStore(
redicers,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
复制代码
将Redux连接到React
import { Provider } from "react-redux";
import store from "./store";
<Provider store={store}>
<App />
</Provider>
复制代码
在React组件中使用
类组件我们使用connect
在类组件我们使用connect
把React
和Redux
连接起来,并通过mapStateToProps、mapDispatchToProps
把state
和操作action
的方法放到组件的props
上。
在组件,我们使用this.props
就能获取到这两个方法里面返回的属性。
import { userAgeIncrementAction } from "../store/actions/userActions";
import { counterIncrementAction } from "../store/actions/counterActions";
class Store extends React.Component{
...
}
// 从store 取 state 放到组件的props里面
const mapStateToProps = (state, ownProps) => {
// 这里的state是store里面的state而不是组件的state
// ownProps也是组件的props,不会包括这里返回的props
console.log(state, ownProps);
return {
user: state.user,
name: state.user.name,
age: state.user.age,
counter: state.counter.value,
};
};
// 从store 取 dispatch 然后触发相应action,并把方法放到组件的props里面
const mapDispatchToProps = (dispatch, ownProps) => {
console.log(ownProps);
return {
counterIncremented() {
dispatch(counterIncrementAction());
},
// 传递参数
userAgeIncremented() {
dispatch(userAgeIncrementAction(10));
},
};
};
// 使用connect把React和Redux连接起来
export default connect(mapStateToProps, mapDispatchToProps)(Store);
复制代码
函数组件我们使用useSelector、useDispatch
函数组件使用useSelector、useDispatch
两个Hook
来操作store
。
import { useSelector, useDispatch } from "react-redux";
import { userAgeIncrementAction } from "../store/actions/userActions";
funciton Store() {
const counter = useSelector((state) => state.counter.value);
const age = useSelector((state) => state.user.age);
const dispatch = useDispatch();
const userAgeIncremented = () => {
dispatch(userAgeIncrementAction(5));
};
...
}
复制代码
异步操作需要借助 Redux-Thunk
普通action
返回的是对象,引入Redux-Thunk
后我们的action
可以返回一个带dispatch
参数的函数。
首先需要安装
npm i redux-thunk
复制代码
然后作为中间件配置使用,这里是结合了redux-devtools-extension的使用。
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(redicers, composeEnhancers(applyMiddleware(thunk)));
复制代码
这样我们就可以进行异步操作啦,比如去后台请求数据。
export const userThunk = (payload) => {
return async (dispatch) => {
const res = await fetch( `https://jsonplaceholder.typicode.com/todos/${payload}` );
const response = await res.json();
// 异步请求完后,再触发同步action,更新state
dispatch(userAgeIncrementAction(5));
};
};
复制代码
总体来说,在React
中使用Redux
是非常复杂的。下面我们使用Redux Toolkit
来简化我们Redux
的使用。
Redux Toolkit
使用Redux Toolkit
我们可以简化为以下五步。
- 安装
- 创建slices
- 创建store
- 将Redux连接到React
- 在React中使用
为方便理解,笔者例子文件总体目录结构如下:
store
├── slices
│ ├── counterSlice.js
│ └── userSlice.js
└── index.js
复制代码
安装
首先我们需要安装redux react-redux @reduxjs/toolkit
三个包。
npm i redux react-redux @reduxjs/toolkit
复制代码
创建slices
现在的slices
,就相当于以前的types、actions、reducers
,以对象的形式存在,是不是一目了然。
并且我们不用再返回新state
了,我们可以直接操作state
,它自己内部实现了immutable
的功能。
counterSlice
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0,
},
reducers: {
incremented: (state) => {
state.value += 1;
},
decremented: (state) => {
state.value -= 1;
},
add: (state, action) => {
state.value += action.payload;
},
},
});
// 相当于以前的actions
export const { incremented, decremented, add } = counterSlice.actions;
// 相当于以前的reducers
export default counterSlice.reducer;
复制代码
userSlice
import { createSlice } from "@reduxjs/toolkit";
const userSlice = createSlice({
name: "user",
initialState: {
name: "randy",
age: 24,
},
reducers: {
nameIncrement: (state) => {
state.name += "!";
},
nameDecrement: (state) => {
state.name += state.name.slice(0, state.name.length - 1);
},
ageIncremented: (state, action) => {
state.age += action.payload;
},
ageDecremented: (state, action) => {
state.age -= action.payload;
},
},
});
// 相当于以前的actions
export const { nameIncrement, nameDecrement, ageIncremented, ageDecremented } =
userSlice.actions;
// 相当于以前的reducers
export default userSlice.reducer;
复制代码
创建store
@reduxjs/toolkit
已经封装好了redux-devtools-extension
,我们不用配置直接使用即可。
import { configureStore } from "@reduxjs/toolkit";
import counterSlice from "./slices/counterSlice";
import userSlice from "./slices/userSlice";
const store = configureStore({
reducer: {
counter: counterSlice,
user: userSlice,
},
});
export default store;
复制代码
将Redux连接到React
将Redux
连接到React
这一块没变化。
import { Provider } from "react-redux";
import store from "./store";
<Provider store={store}>
<App />
</Provider>
复制代码
在React组件中使用
使用方法基本没变,只是获取action
变了,以前从actions
里获取,现在是从slice
里获取。
类组件我们使用connect
在类组件我们使用connect
把React
和Redux
连接起来,并通过mapStateToProps、mapDispatchToProps
把state
和操作action
的方法放到组件的props
上。
import { incremented, decremented, add } from "../store/slices/counterSlice";
import { nameIncrement, ageIncremented } from "../store/slices/userSlice";
class Store extends React.Component{
...
}
const mapStateToProps = (state, preProps) => {
return {
counter: state.counter.value,
userName: state.user.name,
userAge: state.user.age,
};
};
const mapDispatchToProps = (dispatch, preProps) => {
return {
handleIncremented() {
dispatch(incremented());
},
handleDecremented() {
dispatch(decremented());
},
handleAdd() {
dispatch(add(10));
},
handleUserName() {
dispatch(nameIncrement());
},
handleUserAge() {
dispatch(ageIncremented(10));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Store);
复制代码
函数组件我们使用useSelector、useDispatch
函数组件使用useSelector、useDispatch
两个Hook
来操作store
。
import { useSelector, useDispatch } from "react-redux";
import { ageIncremented } from "../store/slices/userSlice";
funciton Store() {
const counter = useSelector((state) => state.counter.value);
const age = useSelector((state) => state.user.age);
const dispatch = useDispatch();
const userAgeIncremented = () => {
dispatch(ageIncremented(5));
};
...
}
复制代码
异步操作可以直接使用Redux-Thunk
@reduxjs/toolkit
已经内置了Redux-Thunk
,不需要另外安装和配置。
我们只需要使用@reduxjs/toolkit
的createAsyncThunk
就能完成异步action
的创建。
import { createAsyncThunk } from "@reduxjs/toolkit";
// 创建异步action
export const userThunk = createAsyncThunk(
"user/thunk",
async (payload, thunkAPI) => {
console.log(payload);
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${payload}`
);
const response = await res.json();
// 调用普通action
thunkAPI.dispatch(ageIncremented(8));
// 返回值会作为action返回对象payload属性的值。
return response;
}
);
复制代码
createAsyncThunk
创建成功后,return
出去的值,会在extraReducers
中接收,有三种状态:
- pending: 运行中;
- fulfilled: 完成;
- rejected: 拒绝;
我们可以在这里面把我们异步请求的数据存放到state
中。
ducers: (builder) => {
builder
.addCase(userThunk.pending, (state, action) => {
console.log(state, action);
})
.addCase(userThunk.fulfilled, (state, action) => {
console.log(state, action);
// state.lists = action.payload // 数据存储到state
})
.addCase(userThunk.rejected, (state, action) => {
console.log(state, action);
});
},
复制代码
如果不想存放到state
里,而是想直接获取也是可以的。createAsyncThunk
第二个函数的返回值也会作为action
返回对象payload
属性的值。
import { userThunk } from "../store/slices/userSlice";
...
const mapDispatchToProps = (dispatch, preProps) => {
return {
// thunk
async handleUserThunk() {
// 这里也是能获取到异步请求的结果的。
const res = await dispatch(userThunk(1));
console.log(res); // {meta: xxx, payload: userThunk的返回值response, type: "user/thunk/fulfilled"}
},
};
};
复制代码
本文只是简单介绍了@reduxjs/toolkit
的使用,算是一个入门吧,详细使用可以查看Redux Toolkit 官方文档。
总结
Redux Toolkit
优点:
-
Redux Toolkit
已经集成了redux-devtools-extension
,不需要额外配置,非常方便。 -
Redux Toolkit
已经集成了immutable-js
的功能,不需要我们额外安装配置使用,大大提升了开发效率。 -
Redux Toolkit
已经集成了redux-thunk
的功能,不需要我们额外安装配置使用,大大提升了开发效率。 -
Redux Toolkit
将types、actions、reducers
放在了一起组成全新的slices
一目了然简单易懂,简化了我们的使用。
参考文档
系列文章
React-Router6路由新特性(React-Router4/5和React-Router6对比总结)
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!