1.redux介绍
react和vue都已经全面拥抱了hooks,想必大家在使用react函数组件的时候导入redux学习时,却发现网上的redux资料大都是基于react的类组件,这时心里会有许多蛋蛋的忧伤。不要怕,经过多方面的搜集整理,我将介绍如何在函数组件中正确使用redux,本人实力有限,如果介绍不当请多多批评。
2.redux安装和配置
下载
npm i redux
项目初始化
store目录下创建index.js
import {legacy_createStore} from "redux"
//初始的数据
let num = 1
//reducer控制数据的修改
function reducers(state=num,action) {
switch(action.type) {
case "add":
return state+action.num
case "del":
return state-action.num
default:return state
}
}
//核心store
const store = legacy_createStore(reducers)
export default store
根目录下的index.js中导入store
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {Provider } from "react-redux"
import store from "./store/index"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
使用redux数据
函数组件中使用redux组件十分十分的简单,只需要导入useSelector就可以让redux的数据具备响应式,通过useDispatch我们可以完成redux数据的修改操作。
import {useDispatch, useSelector} from "react-redux"
export default function() {
const dispatch = useDispatch()
const num = useSelector((state)=>{
return state
})
return <div>
<div>{num}</div>
<div onClick={()=>dispatch({type:'add',num:1})}>增加num</div>
<div onClick={()=>dispatch({type:'del',num:1})}>减少Num</div>
</div>
}
页面效果
3.多个reducer的问题
看完上面的问题,大家感觉函数组件的redux操作是不是很简单,但是实际我们还有一部分问题没有解决,假设我们现在有多个模块的数据,例如用户信息数据,产品详情数据等。如果我们使用一个reducer函数的switch去操作redux数据,出现的问题如下代码所示,switch判断和数据变得十分臃肿,不利于代码的维护和阅读。
const data = {
user:{},
list:{}
}
function reducer(state=data,actions) {
switch(actions.type) {
case "xx" :
case "xx" :
case "xx" :
.....
}
}
对于上面的问题,我们通常需要对reducer进行拆分,通常一个模块对应一个reducer,最后我们通过api进行reducer的合并就可以了。
1. store目录下新建reducer文件夹
2. reducer文件夹下存放不同的reudcer
我们在reducer文件夹下创建了两个reducer,每个reducer都有自己的数据和自己的数据处理方法。如下
books.js
let num = 1
export default function reducers(state=num,actions) {
switch(actions.type) {
case "addbook":
return state+actions.num
case "delbook":
return state-actions.num
default:
return state
}
}
user.js
let num = 2
export default function reducers(state=num,actions) {
switch(actions.type) {
case "adduser":
return state+actions.num
case "deluser":
return state-actions.num
default:
return state
}
}
3. store/reudcer/index.js下进行多个reducer的合并操作
import books from "./books";
import user from "./user";
import {combineReducers} from "redux"
export default combineReducers({
books,user
})
4. store/index.js配置
import {legacy_createStore } from 'redux'
import rootReducer from "./reducers/index"
export default legacy_createStore(rootReducer)
页面数据的测试
import {useDispatch, useSelector} from "react-redux"
export default function() {
const dispatch = useDispatch()
//user模块
const numUsers = useSelector((state)=>{
return state.user
})
//books模块
const numBooks = useSelector((state)=>{
return state.books
})
return <div>
<div>
<p>user模块</p>
<div>num:{numUsers}</div>
<div></div>
<div onClick={()=>dispatch({type:'adduser',num:1})}>增加num</div>
<div onClick={()=>dispatch({type:'deluser',num:1})}>减少Num</div>
</div>
<div>---------------------------------</div>
<div>
<p>books模块</p>
<div>num:{numBooks}</div>
<div></div>
<div onClick={()=>dispatch({type:'addbook',num:1})}>增加num</div>
<div onClick={()=>dispatch({type:'delbook',num:1})}>减少Num</div>
</div>
</div>
}
我们设置了两个不同模块的reducer,并且各自有各自的数据,尽管它们都是num,但是这个num是属于不同模块的,是互相独立的数据。如下是两个模块的测试,可以看到两个模块的num是各自独立的。
4.dispatch的优化
观察上面dispatch部分,传入一个对象到redux的reducer中,这部分出现了大量重复臃肿设计代码,如果reducer内部发生了改变,我们需要对这些dispatch部分进行逐个的修改。因此在日常开发中,我们会对dispatch传递一个纯函数,函数内部返回一个对象。
旧的设计
dispatch({type:"addbook",num:1})
改造后设计
function addbook(num) {
return {
type:'addbook"
num:num
}
}
dispatch(addbook(1)
项目中的设计
通常我们会在store目录下创建一个actions文件夹保存不同模块的actions函数
actions/user.js
export function adduser(num) {
return {
type:"adduser",
num
}
}
export function deluser(num) {
return {
type:"deluser",
num
}
}
项目中使用
import {useDispatch, useSelector} from "react-redux"
import {adduser,deluser} from "./../store/actions/user"
export default function() {
const dispatch = useDispatch()
//user模块
const numUsers = useSelector((state)=>{
return state.user
})
return <div>
<div>
<p>user模块</p>
<div>num:{numUsers}</div>
<div></div>
<div onClick={()=>dispatch(adduser(1))}>增加num</div>
<div onClick={()=>dispatch(deluser(1))}>减少Num</div>
</div>
</div>
}
5.actions-type引入
经过上面的优化,已经完成了99%的工作,还差1%就是优化type参数的传递,上面的type我们都是传递一个字符串,但是实际开发中,随着项目工作量的加剧,很容易在传递type字符串的时候出现错误。因此我们需要使用变量代替字符串,这样我们就可以保证唯一性和正确性。通常在store目录下创建一个type文件夹保存所有的type
store/types/books.js
export const ADDBOOK = "addbook"
export const DELBOOK = 'delbook'
store/reducer/books.js
import {ADDBOOK,DELBOOK} from "../types/books"
let name = 1
export default function(state=name,action) {
switch(action.type) {
case ADDBOOK:
return state+action.num
case DELBOOK:
return state-action.num
default: return state
}
}
store/actions/books.js
import {ADDBOOK,DELBOOK} from "./../types/books"
export function addbook (num) {
return {
type:ADDBOOK,
num
}
}
export function delbook (num) {
return {
type:DELBOOK,
num
}
}
使用
import {useDispatch, useSelector} from "react-redux"
import {addbook,delbook} from "./../store/actions/books"
export default function() {
const dispatch = useDispatch()
const numBooks = useSelector((state)=>{
return state.books
})
return <div>
<div>
<p>books模块</p>
<div>num:{numBooks}</div>
<div></div>
<div onClick={()=>dispatch(addbook(1))}>增加num</div>
<div onClick={()=>dispatch(delbook(1))}>减少Num</div>
</div>
</div>
}
6.redux-thunk引入(处理异步过程)
终于来到最后一部分了,如果我们需要在redux引入异步修改数据时,如何解决?此处我们需要使用一个中间件 redux-thunk,有了这个中间件,我们可以在dsipatch修改redux数据的时候,传递一个函数,这个函数内部就可以执行异步修改redux数据的操作。
下载
npm i redux-thunk
store/index.js配置
import { applyMiddleware,legacy_createStore } from 'redux'
import rootReducer from "./reducers/index"
import thunk from "redux-thunk"
export default legacy_createStore(rootReducer,applyMiddleware(thunk))
store/actions/user.js
我们按照上面的规范,在actions中操作dispatch传递的参数。我们直接在books模块添加一个异步模拟。
import {ADDBOOK,DELBOOK} from "./../types/books"
export function addbook (num) {
return {
type:ADDBOOK,
num
}
}
export function delbook (num) {
return {
type:DELBOOK,
num
}
}
//异步的actions
export function asyncAdd(num) {
return function(dispatch) {
setTimeout(() => {
dispatch(addbook(num))
}, 2000);
}
}
使用
import {useDispatch, useSelector} from "react-redux"
import {addbook,delbook,asyncAdd} from "./../store/actions/books"
export default function() {
const dispatch = useDispatch()
const numBooks = useSelector((state)=>{
return state.books
})
return <div>
<div>
<p>books模块</p>
<div>num:{numBooks}</div>
<div></div>
<div onClick={()=>dispatch(addbook(1))}>增加num</div>
<div onClick={()=>dispatch(delbook(1))}>减少Num</div>
<div onClick={()=>dispatch(asyncAdd(100))}>异步添加</div>
</div>
</div>
}
测试
可以看到,当我们点击异步添加的时候,页面在2s后修改了redux的数据。
总结
希望大家认真练习相关redux的操作和规范化设计,如果有任何疑问,评论区留言啊。