首页 >> 大全

React系统学习笔记--超基础--超详细--超简洁--Redux(七)

2024-01-09 大全 27 作者:考证青年

PartⅦ:Redux 1 redux理解

学习文档

英文文档:

中文文档:

redux是什么

redux是一个专门用于做状态管理的JS库(不是react插件库)

它可以用在react,,vue等项目中,但基本与react配合使用

作用:集中式管理react应用中多个组件共享的状态

什么情况下需要使用redux

某个组件的状态,需要让其他组件可以随时拿到(共享)

一个组件需要改变另一个组件的状态(通信)

总体原则:能不用就不用, 如果不用比较吃力才考虑使用

redux工作流程

2 redux的三个核心概念

动作的对象

包含2个属性

type:标识属性, 值为字符串, 唯一, 必要属性

data:数据属性, 值类型任意, 可选属性

例子:{ type: ‘’,data:{name: ‘tom’,age:18} }

用于初始化状态、加工状态,初始化时传入的是加工时,根据旧的state和, 产生新的state的纯函数(以下为纯函数概念) redux的函数必须是一个纯函数

store

将state、、联系在一起的对象

如何得到此对象?

此对象的功能?

3 redux的核心API

()与()

()作用:创建包含指定的store对象,目前已被弃用(2022.11)

()作用:应用上基于redux的中间件(插件库)

store对象

作用: redux库最核心的管理对象

它内部维护着:

核心方法:

具体编码:

()

作用:合并多个函数

//代码示例
------------------ redux/reducers/index.js ------------------------------------
/*** 该文件用于汇总所有的reducer为一个总的reducer*/
//引入combineReducers,用于汇总多个reducer
import {combineReducers} from 'redux'
//引入为Count组件服务的reducer
import count from './count'
import persons from './person'//汇总所有的reducer变为一个总的reducer
export default combineReducers({count,persons
})

4 redux基础求和案例

redux精简版案例

src下准备好redux文件夹,文件下有store.js和.js

store.js

//新写法,创建redux最为核心的store对象
import { legacy_createStore as createStore } from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'export default createStore(countReducer)

.js

//初始化状态---注意现在状态不放在组件里
const initState = 99
export default function countReducer(preState = initState, action) {//从action对象中获取:type,dataconst { type, data } = action//根据type决定如何加工数据switch (type) {case 'increment':return preState + datacase 'decrement':return preState - datadefault:return preState}}

Count.jsx组件

//获取redux中的状态getState
<h1>当前求和为:{store.getState()}</h1>//引入store
import store from '../../redux/store'//允许组件还有自己的状态
state = { car: '保时捷' }//redux更新完状态需要重新渲染,这样写不是最佳方案,因为如果组件多的话每个都要写
//可以在入口文件中引入store,将App挂载的时候包裹在store.subscribe里面,状态一变化,App组件都将重新渲染
componentDidMount() {//监测redux中状态的变化,只要变化,就调用renderstore.subscribe(() => {this.setState({})})
}increment = () => {const { value } = this.selectNumberstore.dispatch({ type: 'increment', data: value * 1 })
}
decrement = () => {const { value } = this.selectNumberstore.dispatch({ type: 'decrement', data: value * 1 })
}

入口文件index.js

// 状态改变重新渲染 App 组件
store.subscribe(() => {ReactDOM.render(<App />, document.getElementById('root'))
})

备注:redux只负责管理状态,状态改变驱动页面显示需要我们自己来

redux完整版案例

多了两个文件.js和.js

.js

//该模块用于定义action对象中type类型的常量值,以防单词写错
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

.js

//该文件专门为Count组件生成action对象
import { INCREMENT, DECREMENT } from "./constant"export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })

Count.js组件

//不用自己写action对象,直接调用
increment = () => {const { value } = this.selectNumberstore.dispatch(createIncrementAction(value * 1))
}
decrement = () => {const { value } = this.selectNumberstore.dispatch(createDecrementAction(value * 1))
}

异步的redux版本–不在Count组件中设置定时

有两种形式:{}同步,异步

store默认不允许给的值不是一般对象(简单来说就是store最终接收的必须是一个合法的包含type和data的一般对象),给函数会报错滴,这时需要一个中间件来解决redux-thunk

npm i redux-thunk

store.js

//redux中的执行中间件函数applyMiddleware
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk中间件,用来支持异步action
import thunk from 'redux-thunk'
//暴露store,第二个参数是执行异步的中间件
export default createStore(countReducer, applyMiddleware(thunk))

有了中间件store才会帮你调异步的函数,异步中一般都会调用同步

.js

import { INCREMENT, DECREMENT } from "./constant"
import store from "./store"
//同步action,action的值是object一般对象
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
//异步action,action的值是函数--因为函数可以开启异步任务
export const createIncrementAsyncAction = (data, time) => {return () => {setTimeout(() => {store.dispatch(createIncrementAction(data))}, time)}
}//其实还可以这样写,因为是store来调这异步函数,所以不用引入store
//因为store帮我们调的异步函数,所以知道等下要用到dispatch,store帮你传过来了
import { INCREMENT, DECREMENT } from "./constant"
//同步action,action的值是object一般对象
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
//异步action,action的值是函数--因为函数可以开启异步任务
export const createIncrementAsyncAction = (data, time) => {return (dispatch) => {setTimeout(() => {dispatch(createIncrementAction(data))}, time)}
}

Count.js组件

incrementAsync = () => {const { value } = this.selectNumber// setTimeout(() => {store.dispatch(createIncrementAsyncAction(value * 1, 500))// }, 500)
}

5 react-redux

理解

一个react插件库

专门用来简化react应用中使用redux

react-Redux将所有组件分成两大类

UI组件

只负责 UI 的呈现,不带有任何业务逻辑

通过props接收数据(一般数据和函数)

不使用任何 Redux 的 API

一般保存在文件夹下,也可以直接写在容器组件中直接加工成容器组件

容器组件

负责管理数据和业务逻辑,不负责UI的呈现

使用 Redux 的 API

一般保存在文件夹下

容器组件需要实现很多功能,需要借助东西去生成

注意:store不是在容器组件中直接引入,而是在App组件中通过props传过来的

UI组件是看不见任何redux的AIPI

求和案例react-redux

首先准备好UI组件(一般放在src下的)和容器组件(一般放在src下的)

容器组件Count下的index.js,注意store不在这里引入

//引入Count的UI组件
import CountUI from "../../components/CountUI";
//引入action
import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } 
from "../../redux/count_action";
//引入connect用于链接UI组件和redux
import { connect } from 'react-redux'
/* mapStateToProps函数返回的是一个对象
对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value---容器组件把从redux中获取的状态传给UI组件
mapStateToProps用于传递状态
注意这边不需要亲自引入store,react-redux调用mapStateToProps函数时帮我们传入state了*/
function mapStateToProps(state) {return { count: state }
}
/* mapDispatchToProps函数返回的是一个对象
对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
mapDispatchToProps用于传递操作状态的方法*/
function mapDispatchToProps(dispatch) {return {jia: (number) => {//通知redux执行加法dispatch(createIncrementAction(number))},jian: (number) => {//通知redux执行减法dispatch(createDecrementAction(number))},jiaAsync: (number, time) => {dispatch(createIncrementAsyncAction(number, time))}}
}//创建Count容器组件
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(CountUI)export default CountContainer

UI组件

//从props中读取
<h1>当前求和为:{this.props.count}</h1>//UI组件中不会有任何redux的APIincrement = () => {const { value } = this.selectNumberthis.props.jia(value * 1)}decrement = () => {const { value } = this.selectNumberthis.props.jian(value * 1)}incrementIfOdd = () => {const { value } = this.selectNumberif (this.props.count % 2 !== 0) {this.props.jia(value * 1)}}incrementAsync = () => {const { value } = this.selectNumberthis.props.jiaAsync(value * 1, 500)}

App.js

import store from './redux/store'
//把store传递给容器组件
<Count store={store} />

优化一:简写

//创建Count容器组件---第二个函数可以写为对象,react-redux会帮我们自动分发dispatch
export default connect(state => ({ count: state }),{jia: createIncrementAction,jian: createDecrementAction,jiaAsync: createIncrementAsyncAction}
)(CountUI)

优化二:组件使用

使用了react-redux之后可以不写监测redux中状态变化的代码,因为创建容器组件的时候就有了监测redux状态变化的能力

所以之前为什么不是自己创建容器组件,因为创建的可以实现自动监测redux中状态的变化

//入口文件index.js---可以不用写了
store.subscribe(() => {root.render(<App />)
})

如果容器组件太多,我们需要在App.jsx中一个一个去传store,太繁琐,有没有什么方法可以优化

在入口文件中用组件,可以只写一次,用到store的容器组件都会被提供store

//入口文件index.js
import { Provider } from 'react-redux';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Provider store={store}><App /></Provider>
);

优化三:整合UI组件和容器组件

一个文件可以定义n个组件

定义了UI组件之后直接定义容器组件并使用,最终暴露容器组件

知识点小结

()()

作用: 用于包装 UI 组件生成容器组件

使用(,)(UI组件)

注意点:

作用:将外部的数据(即state对象)转换为UI组件的标签属性

函数返回的是一个对象;

返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value

用于传递状态

作用:将分发的函数转换为UI组件的标签属性

函数返回的是一个对象;

返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value

用于传递操作状态的方法

可以省略,直接传入,api将会自动调用

数据共享—重点

多个组件的数据都放在redux中,redux用对象来存储,需要借助redux提供的先进行合并,合并之后的总状态是一个对象

react基础教程__react基础入门

容器组件在中取状态,交给子组件UI展示

现在有和Count组件,进行数据共享

为组件创建、、

export const ADD_PERSON = 'add_person'

import { ADD_PERSON } from '../constant'//创建增加一个人的action动作对象
export const createAddPersonAction = personObj => ({ type: ADD_PERSON, data: personObj })

//对人状态的初始化以及加工
//引入常量
import { ADD_PERSON } from '../constant'
//初始化状态
const initState = [{ id: '001', name: 'tome', age: 18 }]
//加工状态
export default function personReducer(preState = initState, action) {const { type, data } = actionswitch (type) {case ADD_PERSON:return [data, ...preState]default:return preState}
}

需要在store.js中进行合并

//新写法,创建redux最为核心的store对象
import { legacy_createStore as createStore, applyMiddleware, combineReducers } from 'redux'
//引入为Count组件服务的reducer
import countReducer from './reducers/count'
//引入为Person服务的reducer
import personReducer from './reducers/person'
//引入redux-thunk中间件,用来支持异步action
import thunk from 'redux-thunk'//借助personReducer汇总所有reducer
const allReducer = combineReducers({//谁以后可以初始化和加工这个hehe: countReducer,rens: personReducer
})//暴露store
export default createStore(allReducer, applyMiddleware(thunk))

组件—创建容器链接UI组件和redux

import React, { Component } from 'react'
import { nanoid } from 'nanoid'
import { connect } from 'react-redux'
import { createAddPersonAction } from '../../redux/actions/person'class Person extends Component {addPerson = () => {//获取用户输入const name = this.nameNode.valueconst age = this.ageNode.valueconst personObj = { id: nanoid(), name, age }this.props.jiaRen(personObj)this.nameNode.value = ''this.ageNode.value = ''}render() {return (<div><h2>我是Person组件</h2><input ref={c => this.nameNode = c} type="text" placeholder='输入名字' /><input ref={c => this.ageNode = c} type="text" placeholder='输入年龄' /><button onClick={this.addPerson}>添加</button><ul>{this.props.yiduiren.map((p) => {return <li key={p.id}>名字{p.name}--年龄{p.age}</li>})}</ul></div>)}
}export default connect(//state就是redux帮我们保存的总状态state => ({ yiduiren: state.rens }),{jiaRen: createAddPersonAction})(Person)

同样的App.js中需要引入组件并展示

<Count />
<Person />

纯函数

在中如果是一个数组,不可以用push、等方法进行修改,如此修改并不会修改其引用,所以diff并不会判定其发生改变,导致页面无法自动重新渲染

//浅比较,redux不认为是新数组--而且这样子写reducer就不是一个纯函数了--这边就是改写了参数preState的数据
preState.unshift(data)
return preState
//返回新数组可以
return [data, ...preState]

纯函数定义:只要是同样的输入(实参),必定得到同样的输出(返回)

遵守以下一些约束:

redux的函数必须是一个纯函数

6 使用redux调试工具

安装浏览器插件

Redux

下载工具依赖包

npm i redux--

修改store.js

{} from 'redux--'

/**
* 该文件撰文用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入汇总后的reducer
import reducer from './reducers'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//引入redux-devtools-extension
import {composeWithDevTools} from 'redux-devtools-extension'
//暴露store
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))

7 求和案例的最终代码

src文件目录

src

​ --Count

​ --index.jsx

​ --

​ --index.jsx

–redux

​ --

​ --count.js

​ --.js

​ --

​ --count.js

​ --index.js

​ --.js

​ --.js

​ --store.js

–App.jsx

–index.js

index.js

import React from 'react'
import ReactDOM from "react-dom"
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'ReactDOM.render(/* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */<Provider store={store}><App/></Provider>,document.getElementById('root')
)

App.jsx

import React, { Component } from 'react'
import Count from './containers/Count' //引入的Count的容器组件
import Person from './containers/Person' //引入的Person的容器组件export default class App extends Component {render() {return (<div><Count/><hr/><Person/></div>)}
}

redux文件

文件夹

--------------------------------count.js------------------------------------------
/**
* 该文件专门未Count组件生成对象
*/
import {INCREMENT,DECREMENT} from '../constant'//声明同步action,就是指action的值为Object类型的一般对象
export const increment=data=>({type:INCREMENT,data})
export const decrement=data=>({type:DECREMENT,data})//声明异步action,就是指action的值为函数,异步action中一般都会调用同步action
//在外部调用该action方法时需要引入redux-thunk,用于支持异步action
//该方法会自动传入dispatchexport const incrementAsync=(data,time)=>{return (dispatch)=>{setTimeout(()=>{dispatch(increment(data))},time)}}
--------------------------------------person.js-------------------------------
import {ADD_PERSON} from '../constant'
//创建增加一个人的action动作对象
export const addPerson=personObj=>({type:ADD_PERSON,data:personObj
})

文件夹

--------------------------------count.js------------------------------------------
/**
* 1. 该文件时用于创建一个为Count组件服务的reducer.reducer的本质就是一个函数
* 2. reducer函数会接到两个参数,分别为:之前状态(preState),动作对象(action)
*/
import {INCREMENT,DECREMENT
} from '../constant'
const initState = 0 //初始化状态
export default function countReducer(preState = initState, action) {//从action对象中获取:type:dataconst {type,data} = action//根据type决定如何加工数据switch (type) {case INCREMENT:return preState + datacase DECREMENT:return preState - datadefault:return preState}
}
--------------------------------------person.js-------------------------------
import {ADD_PERSON} from '../constant'
//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]
export default function personReducer(preState=initState,action){// console.log('personReducer@#@#@#');const {type,data} = actionswitch (type) {case ADD_PERSON: //若是添加一个人//preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。return [data,...preState]default:return preState}
}
--------------------------------------index.js-------------------------------
/**
* 该文件用于汇总所有的reducer为一个总的reducer
*/
//引入combineReducers,用于汇总多个reducer
import {combineReducers} from 'redux'
//引入为Count组件服务的reducer
import count from './count'
import persons from './person'//汇总所有的reducer变为一个总的reducer
export default combineReducers({count,persons
})

store.js

/**
* 该文件撰文用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入汇总后的reducer
import reducer from './reducers'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
//引入redux-devtools-extension
import {composeWithDevTools} from 'redux-devtools-extension'
//暴露store
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))

4..js

/**
* 该模块是用于定义action对象中的type类型的常量值,目的只有一个:
*  便于管理的同事防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'

Count文件夹的index.jsx

  import React, { Component } from 'react'//引入actionimport {increment,decrement,incrementAsync} from "../../redux/actions/count"//引入connect用于链接UI组件与reduximport { connect } from 'react-redux'//定义UI组件,这个将再connect()()中加工成容器组件,就可以调用到其传入的redux状态与actionsclass Count extends Component {increment = () => {//获取出入内容const { value } = this.selectNumberthis.props.increment(value * 1)}//减法decrement = () => {const { value } = this.selectNumberthis.props.decrement(value * 1)}//奇数再加incrementIfOdd = () => {const { value } = this.selectNumberif (this.props.count % 2 !== 0) {this.props.increment(value * 1)}}//异步加incrementAsync = () => {const { value } = this.selectNumberthis.props.incrementAsync(value * 1, 500)}render() {return (<div><h2>我是Count组件,下方组件总人数为:{this.props.personCount}</h2><h4>当前求和为:{this.props.count}</h4><select ref={c => this.selectNumber = c}><option value="1">1</option><option value="2">2</option><option value="3">3</option></select>&nbsp;<button onClick={this.increment}>+</button>&nbsp;<button onClick={this.decrement}>-</button>&nbsp;<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;<button onClick={this.incrementAsync}>异步加</button>&nbsp;</div>)}}//使用connect()()创建并暴露一个Count的容器组件//使用connect(传入状态,操作状态方法)(UI组件)export default connect(state => ({count: state.count,personCount: state.persons.length}),{increment, decrement, incrementAsync})(Count)

文件夹下的jsx

  import React, { Component } from 'react'import { connect } from 'react-redux'import { addPerson } from '../../redux/actions/person'import { nanoid } from 'nanoid'//创建UI组件class Person extends Component {addPerson = () => {const name = this.nameNode.valueconst age = this.ageNode.value * 1const personObj = { id: nanoid(), name, age }this.props.addPerson(personObj)this.nameNode.value = ''this.ageNode.value = ''}render() {return (<div><h2>我是Person组件,上方组件求和为{this.props.count}</h2><input ref={c => this.nameNode = c} type="text" placeholder="输入名字" /><input ref={c => this.ageNode = c} type="text" placeholder="输入年龄" /><button onClick={this.addPerson}>添加</button><ul>{this.props.persons.map((p) => {return <li key={p.id}>{p.name}--{p.age}</li>})}</ul></div>)}}export default connect(state => ({persons: state.persons,count: state.count}), { addPerson })(Person)

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了