redux

redux

为什么需要 Redux

组件之间的通信,数据流是单向的,从顶层组件通过props向下传递数据,下层组件是不可以向上传递数据,如果需要这时下层组件需要修改传递的数据,就需要通过顶层的组件传递过来一个方法,在下层组件进行使用,当项目越来越大的时,组件之间的传递数据的操作,会变得困难,代码看起来逻辑不够清晰。

redux在react中担当一个数据管理的模块

Redux是什么

使用场景

  • 同一个 state 需要在多个 Component 中共享
  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变另一个组件的状态
  • 一个组件需要改变全局状态

安装

npm install redux
复制代码
npm install react-redux
复制代码

store使用

PS:store是独立的,书写在入口文件内(index.js)

创建store

  1. Redux 提供createStore这个函数,用来生成 Store
  2. createStore函数接受另一个函数作为参数,返回新生成的 Store 对象
// 在redux里面键方法取出来,用于创建store
inport { createStore } from 'redux'

// 方法名称自定义
function fn() {
   // 具体操作
}

const store = createStore(fu)
复制代码

获取store数据

  • 传递数据
  1. 在react-redux导出方法,使用它将组件进行包裹,类似于属性传参的形式将store传递过去
// 用导出的方法,将组件进行包裹,使用
import { Provider } from 'react-redux'

<Provider store={ store }> <App> </Provider>
复制代码

Snipaste_2021-04-27_10-47-21.png

  • 在某一个组件内部进行接收
  1. 需要再次使用react-redux里面的工具
  2. 对方法进行调用,调用的时候可以传递一个参数,在参数内部可以返回一个数据,也可以获取数据,最后这些数据会以props的形式传给组件,语法要求当前函数调用结束后,还需要返回一个函数,这个函数需要进行调用,接收一个参数,这个参数传递的就是需要接收数据的组件,到底哪一个组件接收传递过来的数据
  3. 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信息,放在单独的一个文件内,每一个组件都会有自己的文件,管理自己的方法,在将方法导出,给组件在使用

Snipaste_2021-04-26_19-35-39.png

const increment = function () {
  return { type: 'increment' }
}
const increment_n = function () {
  return { type: 'increment_n' }
}
export { increment,increment_n }
复制代码
  • 组件内部使用

Snipaste_2021-04-26_19-35-27.png

在导出的时候,可以统一导出,在起别名进行接收

// 别名导出
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内部的数据进行更改,然后处理后的数据重新就会被渲染

Snipaste_2021-04-26_19-55-19.png

  • 为什么进行拆分

因为在一个应用里面,会有很多个组件,每一个组件都有自己的数据,不可能都存放在一个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进行参数传递

总结

文件夹结构

Snipaste_2021-04-27_10-09-26.png

  • Action_types文件夹

该文件夹是存储的是变量,内部是存的是type配置的变量,目的是为了在啊多个地方进行使用,也是为了在之后书写type属性时,导致名字不一致,程序无法正常运行

  • Actions文件夹

是用于存放dispatch配置选项的文件夹,每一个组件都拥有自己的配置选项

  • Raducer文件夹

是用于存放数据与更改数据的方法,内部主要是对数据的处理,与数据的存储,每一个组件对应一个。

Reducer合并后怎么拿数据

export default combinerReducer({
    counter : counterReducer,
    person: personReducer
})
复制代码

Snipaste_2021-04-27_10-17-43.png

数据合并后,打印合并后的结果,根据设置的键名,找到对应的数据进行使用即可

组件内部如何获取数据与修改数据

  • 组件内部是如何获取数据

首先通过connect方法,他会接收两个回调函数,第一个是从store里取出数据,第二个是让react自动生成actions执行函数,通过使用bindActionCreators生成执行函数,函数用于修改数据操作。

Snipaste_2021-04-27_10-27-26.png

参数传递与接收

  • 第一步,通过配置dispatch,在里面可以配置第二个参数,也就是需要传递的数据选项

Snipaste_2021-04-27_10-30-14.png

  • 第二步,组件传递参数

Snipaste_2021-04-27_10-32-09.png

  • 第三步,在处理数据的Reducer里面接收参数,并处理

Snipaste_2021-04-27_10-36-41.png

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)
      }
      复制代码
        1. 先将方法里面的方法导出,方法一是用来拦截操作,方法二是用来返回一个新的指令,相当于触发一个dispatch操作
        2. 导出一个方法,方法内部进行拦截指令,然后第二个参数用来处理请求,并将请求的结果传递出去
        3. 请求数据操作,然后将数据在返回出去
  • 第四步:将多个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的初始值,其他文件正常使用即可

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享