react-Redux的API的使用及原理讲解和手动实现方法

对react-Redux的应用与理解

在平时中,我们直接去使用Redux,需要每个页面都需要引入store,执行getState()来获取到值,以及需要每次都进行订阅和取消订阅。维护起来不方便

import React,{Component} from 'react'
import store from '../store'
export default class ReactRedux extends Component{
  constructor(){
    super()
  }
  componentDidMount(){// 挂载
    this.unsubscribe=store.subscribe(()=>{
      this.forceUpdate()
    })
  }
  add=()=>{    
    store.dispatch({type:'ADD',payload:10})
  }
  componentWillUnmount(){// 卸载
    if(this.unsubscribe){
      this.unsubscribe()
    }
  }
  render(){
    return(
      <div>
        <h3>ReactRedux-page</h3>
        <div>
          <p>{store.getState()}</p>
          <button
            onClick={this.add}
          >
            add
          </button>
        </div>
      </div>
    )
  }
}
store页面

src/store/index.js

import {createStore} from 'redux'

function createReducer(store=1,{type, payload=1}){
  // console.log(store)
  switch (type) {
    case 'ADD':
      return store+payload
      break;
      case 'MINUS':
      return store-payload
      break;  
    default:
      return store
      break;
  }
}
const store = createStore(createReducer)
export default store
从而引入react-Redux,Provider这个功能;在根目录下直接引入store;

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import ReactRedux from './pages/ReactReduxPage'
import {Provider}  from 'react-redux'
import store from './store'

ReactDOM.render(
  <Provider store={store}>
    <ReactRedux />
  </Provider>
 ,
  document.getElementById('root')
);

class组建在使用connect引入

import {connect} from 'react-redux'
connect一共有三个参数:state, dispatch,mergeProps(将props进行合并)
@connect(
  (state)=>({num:state})
)
class ReactRedux extends Component{
    render(){
      console.log(this.props)
      return(
        <div></div>      
      )
   }
}
@connect是装饰器的使用,或者可以export default connect()(class ...);装饰器的使用可以自己查下不做重点讲解。
打印this.props

WX20200707-172454@2x.png

可以看到 传进来的state值,以及dispatch;我们可以在porps中拿到。
import React,{Component} from 'react'
import store from '../store/'
import {connect} from 'react-redux'
@connect(
  (state)=>({num:state})
)
class ReactRedux extends Component{
  constructor(){
    super()
  }
  add=()=>{    
    this.props.dispatch({type:'ADD',payload:10})
  }
  render(){
    const {num, dispatch} = this.props
    console.log(this.props,'this.props')
    return(
      <div>
        <h3>ReactRedux-page</h3>
        <div>
          <p>{num}</p>
          <button
            onClick={this.add}
          >
            add
          </button>
        </div>
      </div>
    )
  }
}
export default ReactRedux
点击的时候直接,直接更新状态;就不再需要去调用生命周期,使用redux的subscribe来进行订阅,更新状态。
WX20200707-173816@2x.png
connectmapDispatchToProps多种写法
dispatch 可以是对象 也可以是函数;

对象写法

@connect(
  //store
  (state)=>({num:state}),
  // dispatch 类型:obj|fn
  {
    add:()=>({
      type:'ADD',
      payload:20
    })
  }
)

函数写法

  dispatch=>{
    const add =()=>dispatch({ type:'ADD', payload:20})
    const minus=()=>dispatch({type:'MINUS', payload:10})
    return {dispatch,add ,minus}
  }

或者使用import bindActionCreators from 'redux'

最后整合出来,的结果和上面的写法是一样的,只是下面使用 bindActionCreators来进行了整合
  dispatch=>{
    // const add =()=>dispatch({ type:'ADD', payload:20})
    // const minus=()=>dispatch({type:'MINUS', payload:10})
    //这个写法和声明注释的写法是一样的
    let creators={
      add:()=>({type:'ADD', payload:20}),
      minus:()=>({type:'MINUS', payload:10})
    }
    creators=bindActionCreators(creators, dispatch)
    return {dispatch,...creators}
  }

props将会上挂载了 add、minus 和dispatch方法;

使用:

 Dispatchadd=()=>{
   this.props.dispatch({type:'ADD',payload:10})
 }

render(){
  const {num, add,minus} = this.props
  console.log(this.props,'this.props')
  return(
    <div>
      <h3>ReactRedux-page</h3>
      <div>
        <p>{num}</p>
        <button onClick={this.Dispatchadd}>Dispatchadd+10</button>
        <button onClick={add}>add+20</button>     
        <button onClick={minus}>minus-10</button>
      </div>
    </div>
  )

mapDispatchToProps 的总结
写法一: function直接可以将dispatch返回;

dispatch=>{
// 在里面定义其他的disaptch方法
return (dispatch, 其他dispatch方法)
}

写法二: object 直接定义dispatch,但是组建中,就只能去调用定义的方法名字,不能去使用this.props.dispatch

 {
   add:()=>({
     type:'ADD',
     payload:20
   })
 }
import React,{Component} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
@connect(
  //将state放在props上一份;
  `参数一: mapStateToProps`
  (state)=>({num:state}),
  `参数二:mapDispatchToProps`
  // dispatch 类型:obj|fn 将 dispatch放在poros上一份; mapDispatchToProps
  // dispatch写法一: 不含本身的dispatch,只能调用对象里面写的方法;
  // {
  //   add:()=>({
  //     type:'ADD',
  //     payload:20
  //   })
  // }
  // dispatch 写法二:
  (dispatch,ownProps)=>{ 
  // ownProps 是指的,当传进来的props进行了修改,每一次都会加载一遍; 
  //ownProps 可以不写,写了只不过是每次当props修改的时候,就会调用这个方法
  
    // 方法一:直接这样写,到导出
    // const add =()=>dispatch({ type:'ADD', payload:20})
    // const minus=()=>dispatch({type:'MINUS', payload:10})
    // return {dispatch, add, minus}
    // 方法二: 使用bindActionCreators 来进行合并
    let creators={
      add:()=>({type:'ADD', payload:20}),
      minus:()=>({type:'MINUS', payload:10})
    }
    creators=bindActionCreators(creators, dispatch)
    return {dispatch,...creators}
  },
  `参数三:mergeprops`,讲解在最后,拿出来说,其实并不怎么常用
  (stateProps,dispatchProps,ownProps)=>{
    return {
      ...stateProps,
      ...dispatchProps,
      ...ownProps
    }
  }
)
class ReactRedux extends Component{
  constructor(){
    super()
  }
  Dispatchadd=()=>{
    this.props.dispatch({type:'ADD',payload:10})
  }
  
  render(){
    const {num, add,minus} = this.props
    console.log(this.props,'this.props')
    return(
      <div>
        <h3>ReactRedux-page</h3>
        <div>
          <p>{num}</p>
          <button onClick={this.Dispatchadd}>Dispatchadd+10</button>
          <button onClick={add}>add+20</button>     
          <button onClick={minus}>minus-10</button>       
        </div>
      </div>
    )
  }
}
export default ReactRedux
connectmergeprops使用说明
 (stateProps,dispatchProps,ownProps)=>{
   return {
     ...stateProps,
     ...dispatchProps,
     ...ownProps
   }
 }

打印 this.props的值
WX20200709-222019@2x.png

dispatch 和 state

当 在return 里面自定义了内容时,props的打印结果

 (stateProps,dispatchProps,ownProps)=>{
   return {
     ...stateProps,
     ...dispatchProps,
     ...ownProps,
     own:'自定义内容'
   }
 }

WX20200709-222312@2x.png

如果在 return里面删除 stateProps,或者dispatchProps;
this.props 也会随之缺失


mapDispatchToProps的函数写法上,去整合 dispatch引入了一个bindActionCreators,现在我们自己实现一下试试。

实现bindActionCreators

import {bindActionCreators} from 'redux'

这个是redux的api,bindActionCreators的作用是将一个或多个action和dispatch组合起来生成mapDispatchToProps需要生成的内容。
function bindActionCreator(creator,dispatch){
// console.log(creator,'creator')
  return (...args) => {
    console.log(args,'...args') // 函数在onClick 调用的时候传递进来的参数
    dispatch(creator(...args))
  }
}

// 接收两个参数 creators, dispatch
export function bindActionCreators(creators, dispatch){
  console.log(creators,'creators')
  // 定一个一个空的对象
  let obj={} // 用来导出
  // 首先需要遍历出来,给每一项加上dispatch;
  for(let key in creators){
    obj[key] = bindActionCreator(creators[key],dispatch)
  }
  console.log(obj,'obj')
  return obj
}

说明:

  • bindActionCreators导出的还是一个对象,是用dispatch整合出来的对象。所以最后return obj
  • for...in 将每一个穿进来的对象,用dispatch在包一层
  • args 是从何而来?

    我们现在修改add 叠加的方式:

       let creators={
         add:(num)=>({type:'ADD',...num}),
         minus:()=>({type:'MINUS', payload:10})
       }

    num 是方法在调用的时候传进来的参数

    class ReactRedux extends Component{
     render(){
       const {num} = this.props
       <button onClick={()=>{add({payload:20})}}>add+20</button> 
     }

    这个时候,我们来打印看看,现在的agrs是什么?
    WX20200709-203043@2x.png
    就是我们在调用add 方法的时候传进来的参数

实现Provider

这个没什么就是用React.createContext();嵌套了一层。这样说话的语气,显得好牛b呀,哈哈哈哈哈哈哈哈,牛死我了
import React from  'react'

const Context= React.createContext()
export function Provider({children, store}){
return <Context.Provider value={store}>{children}</Context.Provider>
}

实现commit

先搭建一个架子出来
import React from  'react'

export const commit=(
  mapStateToProps=state=>state, //这个就是state
  mapDispatchToProps // 这个就是整个出来的dispatch
)=>WrappedComponent=>props=>{ // 最后由组建+props,返回一个新的组建
  return<WrappedComponent {...props}/>
}
组建stateProps的获取
   // 首先要获取到传递进来的stateToProps
   // state的获取是从mapStStateToProps而来的
    const stateProps = mapStateToProps(这里的state从何而来) 
    //mapStateToProps 是一个函数传进来一个state,导出一个state;
mapStateProps的参数state如何获取
首先明确,我们当前的组建,是一个函数组建
那函数组建如何获取state? 通过getState来获取,getState在store里面;结合上下文,我们需要用到useContext来获取;
  // useContext 读取当前的Context;
  const store = useContext(Context);
  // 从store中去获取store
  const {getState} =store
  const stateProps = mapStateToProps(getState())
  return<WrappedComponent {...props} {...stateProps}/>
实现 mapDispatchToProps

初始化已经完成,还需要实现dispatch方法的实现
如何获取dispatch
我们先打印下store

 // useContext 读取当前的Context;
 const store = useContext(Context);
 console.log('store里面的内容:', store)

WX20200710-165955@2x.png
从而得知,dispatch可以从store中获取。
接下来考虑如何更新;dispatch之后组建为更新,所以用订阅subscript 调用forceUpdate来更新组建
在函数组建中,我们使用useReduce 来实现forceUpdate函数,在官网中有记录。
有类似 forceUpdate 的东西吗?:https://zh-hans.reactjs.org/d...
WX20200710-172614@2x.png
还需要主意一个问题,就是当我们去订阅的时候,一定要去取消订阅
我们在什么生命周期里面去调用订阅呢?

useEffect 还是 useLayoutEffect 中去使用?
WX20200710-174111@2x.png
WX20200710-174214@2x.png
看介绍可以得知,useLayoutEffect的调用要比useEffect更为提前
useEffect 存在延迟;如果组建在constructor中就有了订阅和更新,那么useEffect 就会丢失。
所以我们使用useLayoutEffect
const {getState, dispatch, subscribe} =store

const dispatchProps={
    dispatch
  }
  const [ignored, forceUpdate] = useReducer(x=>x+1,0)
  useLayoutEffect(() => {  // 相当于 componentDidMount;useLayoutEffect要比useEffect要提前执行
    // 订阅
    const unsubscribe =subscribe(()=>{ 
      forceUpdate()// 刷新状态
    })
    return () => { // 相当于 componentWillUnmount
      // 取消订阅
      if(unsubscribe){ 
        unsubscribe()
      }
    }
  }, [store]) // 关联store变化时触发
  
  return <WrappedComponent {...props} {...stateProps} {...dispatchProps}/>
实现mapDispatchToProps
  • 从上面的案例得知,mapDispatchToProps有两种展现形式,一种是对象,一种是函数。
  • 我们先打印看下当mapDispatchToProps是函数的情况,是什么?

WX20200711-143823@2x.png

所以 可以得出结论,如果是函数的时候,直接穿参dispatch,执行mapDispatchToProps函数就可以
  • 如果mapDispatchToProps是对象,打印结果
    WX20200711-145650@2x.png
里面是一个对象,我们只需要将它用bindActionCreators进行封装之后在返回就可以。
  let  dispatchProps={ // 定义变成let,下面会根据mapDispatchToProps来进行重复赋植
    dispatch
  }
  // console.log('dispatchProps',dispatchProps);
  // console.log('mapDispatchToProps',mapDispatchToProps)
  // 首先进行类型的判断
   if (typeof mapDispatchToProps === 'function') {
     // 如果是函数,就将diapatch传进去,之后执行函数在返回
      dispatchProps =mapDispatchToProps(dispatch)
   } else if (typeof mapDispatchToProps === 'object'){
     // 如果是对象,就调用bindActionCreators,将对象进行封装返回
     dispatchProps =bindActionCreators(mapDispatchToProps,dispatch)
   }

react-redux的实现完整代码

路径:src/MyReactRedux.js

import React,{useContext,useReducer,useLayoutEffect} from  'react'
import {bindActionCreators} from './MybindActionCreators'
const Context= React.createContext()
export const connect=(
  mapStateToProps=state=>state,
  mapDispatchToProps
)=>WrappedComponent=>props=>{
  
  // useContext 读取当前的Context;
  const store = useContext(Context);
  // 从store中去获取store
  const {getState, dispatch, subscribe} =store

  // 首先要获取到传递进来的stateProps
  // state的获取是从mapStateToProps而来的
  const stateProps = mapStateToProps(getState()) //mapStateToProps 是一个函数传进来一个state,导出一个state;

  let  dispatchProps={ // 定义变成let,下面会根据mapDispatchToProps来进行重复赋植
    dispatch
  }
  // console.log('dispatchProps',dispatchProps);
  // console.log('mapDispatchToProps',mapDispatchToProps)
  // 首先进行类型的判断
   if (typeof mapDispatchToProps === 'function') {
     // 如果是函数,就将diapatch传进去,之后执行函数在返回
      dispatchProps =mapDispatchToProps(dispatch)
   } else if (typeof mapDispatchToProps === 'object'){
     // 如果是对象,就调用bindActionCreators,将对象进行封装返回
     dispatchProps =bindActionCreators(mapDispatchToProps,dispatch)
   }

  const [ignored, forceUpdate] = useReducer(x=>x+1,0)
  useLayoutEffect(() => {  // 相当于 componentDidMount;useLayoutEffect要比useEffect要提前执行
    // 订阅
    // console.log('useLayoutEffect')
    const unsubscribe =subscribe(()=>{ 
      forceUpdate()// 刷新状态
    })
    return () => { // 相当于 componentWillUnmount
      // 取消订阅
      if(unsubscribe){ 
        unsubscribe()
      }
    }
  }, [store]) // 关联store变化时触发
  return <WrappedComponent {...props} {...stateProps} {...dispatchProps}/>
}

export function Provider({children, store}){
 return <Context.Provider value={store}>{children}</Context.Provider>
}

学习资料

github:https://github.com/speak44/le...
分支是:react-redux

阅读 290

推荐阅读