为了防止自己忘记redux相关内容时找不到笔记,记录下来
预:Redux原理理解图
1. redux安装
npm i redux
1.1 redux不分版本与框架,即VUE和React都可以使用
二. Redux初步案例(非全局)
以React项目为例,在React项目的src目录下建立三个不相关JS文件,store/PartA(class组件)/Order(function组件)(名字无实际含义)。
2.1
前往store.js文件中创建store,即声明全局的数据,整个应用需要管理的数据都在store中,也就是store是唯一的,并且store是不能直接修改的,只能触发action返回一个新的store来改变它,
import {createStore} from "redux"
//在redux中引入createStore创建唯一的store
function numReducer(state={num:10},action){
console.log(action);
return state;
}
let store = createStore(numReducer);
export default store;
2.2
前往创建的PartA.js(class组件)文件中调用redux方法查看上文代码块中num值
import React, { Component } from 'react'
import store from "../store";//store存储在同名文件夹下
export default class PartA extends Component {
render() {
return (
<div>PartA
<div>使用全局的数据 {store.getState().num}</div>
//getState()是createStore生成的方法,可直接调用
</div>
)
}
}
输出结果:
2.3
在Order.js文件中查看num
import React from 'react'
import store from "../store"
export default function Order() {
return (
<div>Order
<div>全局数据为{store.getState().num}</div>
//实际上类组件与函数式组件在仅查看上毫无区别
</div>
)
}
输出结果一样
二(1)
修改store与它的值(调用dispath触发action)
import React, { Component } from 'react'
import store from "../store";
export default class PartA extends Component {
render() {
return (
<div>PartA
<div>使用全局的数据 {store.getState().num}</div>
<button onClick={()=>{
store.dispatch({
type:"新增" // 重要核心
})
}}>修改</button>
</div>
)
}
}
返回store.js文件,修改代码
import {createStore} from "redux"
function numReducer(state={num:10},action){
//action.type的值是由个人决定的,是确定的,所以使用switch判断
switch(action.type){
case "新增":
return {num:1000}
default:
return state;
}
}
let store = createStore(numReducer);
export default store;
点击按钮就可以触发action.type的变化,使num由10变为1000
但是!视图却没有同步更新!
因为按钮触发的事件改变的只是num的值,而num的值早在初次渲染的时候就已经给PartA了,修改num又与我PartA有什么关系呢?
所以!需要给PartA添加更新视图方法,class组件的更新方法是setState, newProps forceUdpate
所以给PartA的Button的onClick事件添加任一方法即可
三,全局挂载store
3.1分离方法
在src中新建文件夹用来专门存放Reducer,也就是store的方法,避免造成管理混乱等问题
文件目录图示意
src
----|Store
----|----|index.js
----|Pages
----|----|PartA.js
----|----|Order.js
----|Reducers
----|----|NumReducers.js
将之前写在Store中的function方法挪至新建的NumReducers.js文件中
function numReducer(state={num:10},action){
switch(action.type){
case "新增":
return {num:1000}
default:
return state;
}
}
export default numReducer;
在Store中引入合并
import {createStore,combineReducers} from "redux"
import numReducer from "../Reducers/NumReducer";
let store = combineReducers({
numInfo:numReducer//此后store.numInfo就是一个方法了
})
export default createStore(store);
然后在PartA中调用试试效果
return (
<div>PartA
<div>全局使用store的数据:{store.getState().NumInfo.num}</div>
</div>
)
3.1.1分离action.type
方法已经分离完毕了,那么就继续分离,分离action的type
新建文件夹action
文件目录示意图
src
----|Store
----|----|index.js
----|Pages
----|----|PartA.js
----|----|Order.js
----|Reducers
----|----|NumReducers.js
----|action
----|----|index.js
在action中声明一个常量用来存储action的type值,切记是大写的对象
//例:
const info = {
"ADDNUM":"ADDNUM"
}
export default info;
返回NumReducers.js,修改代码action.type的代码
并新增方法来管理action
import info from "../action";
function numReducer(state={num:10},action){
switch(action.type){
case info.ADDNUM:
return {num:1000}
default:
return state;
}
}
//新增AddNum方法专项管理 ADDNUM
export const AddNum = (payload)=>{
return {
type:info.ADDNUM,
payload
}
}
export default numReducer;
返回PartA页面,使用AddNum方法(使用dispath调用,AddNum也可以传值)
import React, { Component } from 'react'
import store from "../store";
import {AddNum} from "../reducers/numReducer"
export default class PartA extends Component {
render() {
return (
<div>PartA
<div>使用全局的数据 {store.getState().numInfo.num}</div>
<button onClick={()=>{
store.dispatch(AddNum(1))
this.forceUpdate();
}}>修改</button>
</div>
)
}
}
返回NumReducers.js,接收AddNum传来的参数
function numReducer(state={num:10},action){
switch(action.type){
case info.ADDNUM:
return {num:state.num+action.payload}
default:
return state;
}
}
3.2全局挂载
引入react-redux
npm i react-redux
在react的主入口文件中使用react-redux
import React from "react";
import {createRoot} from "react-dom/client"
import {Provider} from "react-redux"
import "./css/app.css"
import App from "./App";
import store from "./store";
let app = createRoot(document.getElementById("root"));
app.render(
<Provider store={store}> //使用Provider包裹根组件,store就是store文件中的store
<App></App>
</Provider>
);
3.2.1 类组件使用
在PartA.js/Order.js中引入connect
import React, { Component } from 'react'
import { connect } from 'react-redux'
export class PartA extends Component {
render() {
return (
<div>PartA</div>
)
}
}
const mapStateToProps = (state) => ({})
const mapDispatchToProps = {}
export default connect(mapStateToProps, mapDispatchToProps)(PartA)
优化一下,添加数据
import React, { Component } from 'react'
import {connect} from "react-redux"
class PartA extends Component {
render() {
return (
<div>PartA
<div>全局的数据 {this.props.num}</div>
</div>
)
}
}
let mapStateToProps = (store)=>{
return {
num:store.numInfo.num
}
}
export default connect(mapStateToProps)(PartA)
再优化一下,添加方法
import React, { Component } from "react";
import { connect } from "react-redux";
import {AddNum} from "../reducers/numReducer"
class PartA extends Component {
render() {
return (
<div>
PartA
<div>全局的数据 {this.props.num}</div>
<button onClick={()=>{
this.props.AddNum(100);
}}>修改</button>
</div>
);
}
}
const mapDispatchToProps = {
AddNum
}
export default connect(({ numInfo }) => ({
num: numInfo.num,
}),mapDispatchToProps)(PartA);
3.2.2函数组件使用
修改Order.js
import React from 'react'
import {useDispatch,useSelector} from "react-redux"
import { AddNum } from '../reducers/numReducer'
export default function Order() {
let num = useSelector(state=>state.numInfo.num);
let dispatch = useDispatch();
return (
<div>Order
<div>全局数据 {num}</div>
<button onClick={(()=>{
dispatch(AddNum(1));
})}>修改数据</button>
</div>
)
}
3.3实践案例
实现顶部Tab导航条的显示与隐藏(类似于手机商城中点击详情页面不显示底部Tab组件的概念)
创建 flagReducer.js
文件目录示意图:
src
----|Store
----|----|index.js //声明全局变量store
----|Pages
----|----|PartA.js //使用的组件A
----|----|Order.js //使用的组件B
----|Reducers
----|----|NumReducers.js //store中控制num的方法
----|----|flagReducer.js //store中控制flag的方法
----|action
----|----|index.js //声明action.type
在flagReducer.js中声明一个方法
function flagReducer(state={flag:true},action){
switch(action.type){
default:
return state;
}
}
export default flagReducer;
在Store文件夹下合并该方法
import {createStore,combineReducers} from "redux"
import numReducer from "../reducers/numReducer";
import flagReducer from "../reducers/flagReducer";
let store = combineReducers({
numInfo:numReducer, //修改store中num值的方法
flagInfo:flagReducer //修改flag是true还是false的方法
})
export default createStore(store);
前往App.js(项目主配置文件)文件中配置全局flag控制路由Tab导航选项
import {useSelector} from "react-redux" //useSelector接收一个回调函数
//根据需要返回一个store的状态组件需要的数据
let flag = useSelector(state=>state.flagInfo.flag);
<div style={{display:flag?"block":"none"}}>
//在App.js的视图结构的最上层添加flag
在action中添加flag专属状态
const info = {
"ADDNUM":"ADDNUM", //控制num的action.type
"CHANGEFLAG":"CHANGEFLAG"//控制flag的action.type
}
export default info;
修改flagReducer.js,使flag的值根据action.type改变,以此达成控制Tab组件显示与否的效果
import info from "../action"
function flagReducer(state={flag:true},action){
switch(action.type){
case info.CHANGEFLAG:
return {flag:action.payload} //flag的状态就是在这里改变的
default:
return state;
}
}
//该函数就是改变flag的函数,不会忘了store只能依靠action.type改变了吧
export const changFlag = (payload)=>{
return {
type:info.CHANGEFLAG, //不要被info.CHANGEFLAG迷惑了哦,这是action.type的
payload
}
}
export default flagReducer;
在需要的地方调用changeFlag函数
比如在详情(Detail.js)页调用达成点击详情页Tab消失,退出详情页Tab出现的效果
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { changFlag } from "../reducers/flagReducer";
export default function Detail() {
let dispatch = useDispatch();
useEffect(() => { //函数式组件中useEffect可以用来模拟生命周期
//页面加载
dispatch(changFlag(false));
return () => {
//页面销毁
dispatch(changFlag(true));
};
}, []);
return <div>Detail</div>;
}