redux
为什么需要 Redux
组件之间的通信,数据流是单向的,从顶层组件通过props向下传递数据,下层组件是不可以向上传递数据,如果需要这时下层组件需要修改传递的数据,就需要通过顶层的组件传递过来一个方法,在下层组件进行使用,当项目越来越大的时,组件之间的传递数据的操作,会变得困难,代码看起来逻辑不够清晰。
redux在react中担当一个数据管理的模块
Redux是什么
使用场景
- 同一个 state 需要在多个 Component 中共享
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变另一个组件的状态
- 一个组件需要改变全局状态
安装
npm install redux
复制代码
npm install react-redux
复制代码
store使用
PS:store是独立的,书写在入口文件内(index.js)
创建store
- Redux 提供
createStore
这个函数,用来生成 Store createStore
函数接受另一个函数作为参数,返回新生成的 Store 对象
// 在redux里面键方法取出来,用于创建store
inport { createStore } from 'redux'
// 方法名称自定义
function fn() {
// 具体操作
}
const store = createStore(fu)
复制代码
获取store数据
- 传递数据
- 在react-redux导出方法,使用它将组件进行包裹,类似于属性传参的形式将store传递过去
// 用导出的方法,将组件进行包裹,使用
import { Provider } from 'react-redux'
<Provider store={ store }> <App> </Provider>
复制代码
- 在某一个组件内部进行接收
- 需要再次使用react-redux里面的工具
- 对方法进行调用,调用的时候可以传递一个参数,在参数内部可以返回一个数据,也可以获取数据,最后这些数据会以props的形式传给组件,语法要求当前函数调用结束后,还需要返回一个函数,这个函数需要进行调用,接收一个参数,这个参数传递的就是需要接收数据的组件,到底哪一个组件接收传递过来的数据
- connect函数里面接收一个参数,一般名字为
state
,这个参数内部存储的就是传递过来的数据
import { connect } from 'react-redux'
// props里面存储的就是,store传递过来的数据
function Head(props) {
return (
<div>{props.count}</div>
)
}
const mod = (state) => ({
// 需要将内部的值进行存储,因为值这个里面存储的数据很多,只拿与组件相关的就可以
count: state.count
})
// 在这里需要导出这个工具函数
export default connect(mod)(Head)
复制代码
修改store数据( action )
通过Action指令,它本身就是一个对象,内部有一个type属性,他的值是一个字符串,就好比当前定义了一个自定义事件,这时候这个指令传递给store,在传递给reducers,在做一些逻辑判断,再将值返回给store。
- 要修改数据的组件内部通过事件或者是其他方式触发
事件内部通过store传递过来的数据内部的dispatch方法,内部就是action,是一个对象,对象使用type属性,传递一个类似于自定义事件的名称,在reduce里面接收传递过来的type值, 对数据进行处理
- 修改数据的组件
import React from 'react'
import { connect } from 'react-redux'
function Counter ( props ) {
return (
<>
<button onClick={ () => {
props.dispatch({ type: 'install' })
} }>+</button>
<p>{ props.count }</p>
<button onClick={ () => {
props.dispatch({ type: 'xxxxxxx' })
} }>-</button>
</>
)
}
const instaiu = (state)=>({
count: state.count
})
export default connect(instaiu)(Counter)
复制代码
- reduce内部接收并进行处理
/* eslint-disable import/no-anonymous-default-export */
const instalssss = {
count: 10
}
export default (state = instalssss,action ) => {
// 这里需要使用switch语句,因为分支可能会很多
switch (action.type) {
case 'install':
return {
count: state.count + 1
}
case 'xxxxxxx':
return {
count: state.count - 1
}
default:
return state
}
}
复制代码
提取action为函数
给connect传入第二个参数
function Counter ( props ) {
return (
<>
// 不需要传参
<button onClick={props.increment}>+</button>
// 需要传递参数
<button onClick={()=> {props.increment_n(5)}}>+</button>
</>
)
}
const mapDispatchToProps = dispatch => ({
// 方法,不需要第二个参数
increment:function () {
dispatch({
type: 'increment'
})
},
// 方法二,需要传递第二个参数
increment_n(payload) {
dispatch({
type: 'increment_n',
payload
})
}
})
export default connect(instaiu,mapDispatchToProps)(Counter)
复制代码
PS: 这里这是的这些方法,都将会传递给props,可以通过props调用进行使用
自动生成action触发函数
通过使用redux内部的方法,设置自动生成action触发函数
- 引入方法
import { bindActionCreators } from "redux"
复制代码
PS: 方法是内部接收两个参数,参数一是一个对象,参数二是dispatch
bindActionCreators({ },dispatch)
复制代码
- 将要配置的action信息,放在单独的一个文件内,每一个组件都会有自己的文件,管理自己的方法,在将方法导出,给组件在使用
const increment = function () {
return { type: 'increment' }
}
const increment_n = function () {
return { type: 'increment_n' }
}
export { increment,increment_n }
复制代码
- 组件内部使用
在导出的时候,可以统一导出,在起别名进行接收
// 别名导出
import * as alink from '../Store/Actions/Counter.actions'
// 将导出的方法接收
import { increment,increment_n } from '../Store/Actions/Counter.actions'
复制代码
在下面进行引用
const mapDispatchToProps = dispatch => (
bindActionCreators({increment, increment_n },dispatch)
)
export default connect(mapStateToProps,mapDispatchToProps)(Counter)
复制代码
设置action类型常量
将type的值拿出来定义成常量,原因是因为这个type的值在多个地方进行使用,也是避免在之后书写时,没有语法提示,容易出错,所以将type类型的值定成常量
export const INCREMENT ='increment'
export const INCREMENT_N ='increment_n'
复制代码
- 哪一个页面用到,就导入使用即可
import { INCREMENT,INCREMENT_N } from '../Action_types/Counter.action.types'
const increment = function () {
return { type: INCREMENT }
}
const increment_n = function () {
return { type: INCREMENT_N }
}
export { increment,increment_n }
复制代码
Reducer合并与拆分
PS:reducer内部在干嘛,从store里面获取数据,再根据相应指令,对store内部的数据进行更改,然后处理后的数据重新就会被渲染
- 为什么进行拆分
因为在一个应用里面,会有很多个组件,每一个组件都有自己的数据,不可能都存放在一个reducer里面,如果进行数据管理,在同一个文件内是比较乱的,所以每一个组件都要创建一个自己的reducer
- 为什么进行合并
多个reducer进行合并使用,redux内部提供的方法进行合并
// 在reducer文件夹内部,穿件一个index.js文件,用于合并多个reducer文件
import { combinerReducers } from 'redux'
// 将每一个reducer进行引入
// 调用方法对数据进行合并
export default combinerReducer({
// 参数接收的是一个对象,为了方便使用,里面的每一个键都与自己的组件的reducer名字相同,方便后期管理
键:导入的reducer //( 键与导入的 名字相同)
})
复制代码
例
import { combinerReducers } from 'redux'
import counterReducer from '路径'
import personReducer from '路径'
export default combinerReducer({
counter : counterReducer,
person: personReducer
})
复制代码
合并后转换成下面的格式
{ counter: {count},person:[{}] }
复制代码
在将后的内容导入,在通过store进行参数传递
总结
文件夹结构
- Action_types文件夹
该文件夹是存储的是变量,内部是存的是type配置的变量,目的是为了在啊多个地方进行使用,也是为了在之后书写type属性时,导致名字不一致,程序无法正常运行
- Actions文件夹
是用于存放dispatch配置选项的文件夹,每一个组件都拥有自己的配置选项
- Raducer文件夹
是用于存放数据与更改数据的方法,内部主要是对数据的处理,与数据的存储,每一个组件对应一个。
Reducer合并后怎么拿数据
export default combinerReducer({
counter : counterReducer,
person: personReducer
})
复制代码
数据合并后,打印合并后的结果,根据设置的键名,找到对应的数据进行使用即可
组件内部如何获取数据与修改数据
- 组件内部是如何获取数据
首先通过connect方法,他会接收两个回调函数,第一个是从store里取出数据,第二个是让react自动生成actions执行函数,通过使用bindActionCreators生成执行函数,函数用于修改数据操作。
参数传递与接收
- 第一步,通过配置dispatch,在里面可以配置第二个参数,也就是需要传递的数据选项
- 第二步,组件传递参数
- 第三步,在处理数据的Reducer里面接收参数,并处理
redux中间件
为什么用中间件
比如说在项目开发过程中,数据不是同步获取的,或者是在处理数据之前有一些异步操作需要完成,这时,通过action发送的事件传动到中间件,让中间件进行处理,然后在返回给store,进行使用。
- 什么时候被触发
就是组件发送action指令时,中间件函数就会触发
注册中间件
- 引入包名
import { createStore,applyMiddleware } from 'redux'
function reducer( state,action ) {
return state
}
// 定义一个中间件函数
function middle() {
// 在内部,规定需要返回一个函数
return function () {
// 异步处理函数,需要返回一个函数
return function () {
// 这里就是,当action被触发的时候,触发,此处是完成异步操作
}
}
}
const store = createStore(reducer,applyMiddleware( 某一个函数名称(middle) ))
复制代码
applyMiddleware,内部书写的是一个函数名称,想要哪一个函数成为中间件,就将函数名书写在参数的位置
中间件函数参数
// 3. 接收两个参数 getState与dispatch
function middle({ getState, dispatch }) { ???作用不清楚20
// 2. 参数作用,就是为了接收下面返回的actio3n
return function (next) {
// 1. 参数接收的是action发送过来的指令
return function ( action ) {
// 模拟异步
setTimeout(() => {
// 如果需要操作一些数据,然后往后面传递,那么就可以通过属性交给action
action.payload = 100
// 在此处操作执行完成之后,还是需要将action交给store
return next(action)
},1000)
}
30
.}
}
复制代码
redux-saga异步决绝方案
点击页面上的按钮,动态的获取服务端的数据
-
第一步:安装包
-
npm install redux-saga 复制代码
-
-
第二步:在入口文件引入saga,并且执行相关的操作
-
import { createStore,applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' 复制代码
-
在使用saga的时候,不是进行直接将这个函数变成中间件,而是先对函数进行调用,返回的值当做中间件
-
const sagaMiddleware = createSagaMiddleware() const store = createStore(toList,applyMiddleware(sagaMiddleware)) 复制代码
-
-
第三步:创建一个单独的文件夹存放Saga配置,因为每一个组件都会有自己独自的saga文件
-
注意的是,如果配置了saga文件,那么action文件内部是不去要进行参数传递的,而是直接在saga文件内部直接将请求的参数进行传递
-
import axios from 'axios' // (一) import { takeEvery, put } from 'redux-saga/effects' // (三) function* loadPerson () { // 处理请求,然后在将请求完的数据返回出去 // - 获取数据 let persons = yield axios.get('http://localhost:3005/api/getUsers').then(res => res.data) // 重新发送一个dispatch yield put({type: 'load_oerson_success',payLoad:persons}) } // (二) export default function* personSage () { // 参数一是接收的哪一个方法 ,参数二,是一个函数,处理要处理的请求等 yield takeEvery('load_person',loadPerson) } 复制代码
-
- 先将方法里面的方法导出,方法一是用来拦截操作,方法二是用来返回一个新的指令,相当于触发一个dispatch操作
- 导出一个方法,方法内部进行拦截指令,然后第二个参数用来处理请求,并将请求的结果传递出去
- 请求数据操作,然后将数据在返回出去
-
-
-
第四步:将多个saga文件进行合并
-
import { all } from 'redux-saga/effects' import personSage from './person.saga' export default function* rootSaga () { yield all([ personSage() //多个文件直接逗号进行书写即可 ]) } 复制代码
-
-
第五步:在reducer文件内部对重新发送的指令进行接收,处理结果
-
function reducer (state = innerText,action) { switch(action.type){ case 'load_oerson_success': return { person: action.payLoad } default: return state } } 复制代码
- 在reducer文件内部case的就不是之前的指令,而是saga文件里面put发送的指令
-
-
第六步:最后在入口文件index里面进行接收,将合并的saga文件进行导出,然后通过run方法,run方法里面接收的是saga文件导出的方法,
-
// 需要书写在store创建之后 // 导出合并后的saga文件 import rootSaga from './Store/Saga/person.saga' sagaMiddleware.run(rootSaga) 复制代码
-
简化action与reducer
安装工具包
npm i redux-actions
复制代码
-
简化actions
-
import { createAction } from 'redux-actions' // expore const increment = () => ({ type: 'increment' }) // 调用方法,将当前想要传递的type类型传递过去 expore const increment_action = createAction('increment_action') 复制代码
-
-
简化reducer
-
// 如果需要和当前配置高名相同,可以改方法起别名进行使用 import { handleAction } from 'redux-action' // 导入指令 import { increment_action } from 'action配置路径' // 初始化数据 const initText = { const :10 } const createReducer = handleAction({ // 如果直接使用导入的指令进行使用,就会被当字符串进行解析,所以直接使用【】对其包裹 [increment_action]:(state,action) => { return // 执行操作 } }, initText) // 再将执行的结果导出 export default createReducer 复制代码
-
参数一是指令,第二个参数是当前reducer的初始值,其他文件正常使用即可
-