react 全家桶

目录

redux 

流程图介绍

redux和react-redux 的关系 

 react-redux

 connect与provider

connect

provider

实现connect+provider

redux中间件


redux 

流程图介绍

redux 是一种数据状态管理模式,就相当于 vuex ,当我们要做的项目中有很多组件需要共享数据时,这时候就可以用 redux 搭建。

 Redux的三个原则

  1.  唯一数据源
  2. 保持状态只读(只能通过调用dispach的形式来修改状态),
  3. 数据改变只能通过纯函数(reducer)完成

redux包含3个比较重要的结构:store reducers actionCreators
 

 创建store

const store = createStore(Reducers)

src/store/index.js 

这里先把把reducer(cournter)和store放到一块,作为简单测试

import { createStore} from 'redux'
//两个参数分别是state,action
const counter  =(state = 0,action)=>{
  switch(action.type){
    case'INCREMENT':
      return state + 1
    case'DECREMENT':
      return state - 1
      default:
        return state
  }
}
const store = createStore(counter)
export default store

src/index.js

注意:store.subscribe() 是 Redux 中的一个函数,用于订阅 store 的变化。当 store 中的 state 发生变化时,订阅函数会被调用。一般情况下,我们可以在订阅函数里调用 store.getState() 来获取最新的 state,然后根据最新的 state 进行相应的操作。

这里采用的是function 形式的render(),方便store.subscribe(render)接收一个函数

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './store'

function render() {
  ReactDOM.render(<App />, document.getElementById('root'))
}
render()
//注意subscribe() 返回一个函数用来注销监听器
store.subscribe(render)

 component/Counter.js

获取数据,store.getState

import React, { Component } from 'react'
import store from '../store'

class Counter extends Component {
  render() {
    return (
      <div>
        当前数值:{store.getState()}
        <button onClick={() => store.dispatch({type:'INCREMENT'})}>+1</button>
        <button onClick={() => store.dispatch({type:'DECREMENT'})}>-1</button>
      </div> 
    )
  }
}

export default Counter

 最后在App.js中挂载子组件Count,

运行


redux和react-redux 的关系 

 首先,redux 是独立的,和react没有什么关系,想要在react中使用redux需要进行一些列的操作把它们连接起来,这一些列的操作即为:react-redux
为了方便使用,Redux 的作者封装了一个 React 专用的库 React-Redux

那么问题来了,既然这样就已经实现了redux,那还需要react-redux干啥? 

思考:如果我要在其他页面也操作这个store,要怎么做?
redux通过编写一些列的代码可以实现这个功能,但是代码量比较大逻辑比较复杂

react-redux 中提供了providerconnect来解决这种问题


 react-redux

 connect与provider

connect 和 provider 是实现store数据共享的关键

connect

作用:提交数据,订阅数据变化的能力

使用方式connect(mapStateToProps, mapDispatchToProps)(Conponents)

在下面的介绍的实现方式中还涉及到组件通信context,而connet 属于Consumer接收的一方

provider

provider作为提供store数据的一方,

同时封装高阶函数取代了redux的subscribe(render)形式

先看下如何使用

reducer

之前我们是把counter和store写在了一起。现在给他单独一个文件counter.js专门处理修改状态

src/reducer/counter.js

const counter = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}
// 处理状态方法
export const mapStateToProps = (state) => {
  console.log(state)
  return {
    //  什么情况下添加reducer的标识 ,当使用combineReducers方法组合reducer
    count: state.counter,
  }
}

// 处理action事件 dispatch
export const mapDispatchToProps = (dispatch) => {
  return {
    increment() {
      // dispatch({}) 表示同步操作,如果是个函数,则是异步
      dispatch({ type: 'INCREMENT' })
    },
    decrement() {
      dispatch({ type: 'DECREMENT' })
    },
  }
}

export default counter

在上面的例子中增加了两个暴露的函数mapStateToProps mapDispatchToProps 

其中mapStateToProps是用来处理state状态,mapDispatchToProps 用来处理action事件,每个action事件都使用了dispatch方式。action事件还包含异步事件,后面会详细介绍。

src/store/index.js

在这里我们将创建好的reducer(counter.js)import引入。store的创建方式依旧是createStore()但是,我们使用的一个新的方法,combineReducers({reducer}).这个函数的作用是将所有的reducer统一整合起来。因为每个实例都只有一个store。而reducer操作不会只有一个。

在上面的reducer 创建当中。我们在处理state的时候。我们使用了标识,来给这些reducer进行区分。例如:这个reducer叫做counter,那我们就在后面加个.counter来区分。count: state.counter,那么在combinReducers的时候就将state中的counter标识作为render 标识

import { createStore, combineReducers, applyMiddleware } from 'redux'
import counter from '../reducer/counter'
//import thunk from 'redux-thunk'
//import logger from 'redux-logger'

const store = createStore(
  combineReducers({ counter }),
  //applyMiddleware(logger, thunk)
)
export default store

src/index.js

这里我们用provider 取代了原来的函数式render,然后将render置入subscribe()的方式

需要注意到的是provider的属性是store而不是context中的value,不要搞混了

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import { Provider,connect } from 'react-redux'
import App from './App'
import store from './store'
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

src/components/Counter.js

在导出的时候,我们使用了connect函数,并将引入的修改状态方法放入,然后传入组件Counter

import React, { Component } from 'react'
import store from '../store'
import { connect } from 'react-redux'
import { mapStateToProps, mapDispatchToProps } from '../reducer/counter'

class Counter extends Component {
  render() {
    const { count, increment, decrement} = this.props
    return (
      <div>
        当前数值:{count}
        <button onClick={() => increment()}>+1</button>
        <button onClick={() => decrement()}>-1</button>
      </div>  
    )
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter)

 看运行,和之前的redux方式写出来的效果是一样的

 那么connect和provider都是如何实现的呢?

实现connect+provider

要实现就要使用到上一章学到的组件通信Context以及高阶函数封装

我们先在src目录下创建一个文件夹react-redux,在这个文件夹下创建connect.js和provider.js文件

首先看看connnect的实现:

// connect(mapStateToProps,mapDiapatchToProps)(Comp)

// 消费者
import React, { PureComponent } from 'react'
import { StoreContext } from './context'

// 提交数据,订阅数据变化的能力
const connect = (mapStateToProps, mapDispatchToProps) => {
  return function enhanceComponent(WrapperComponent) {
    class EnhanceComponet extends PureComponent {
      static contextType = StoreContext
      constructor(props) {
        super(props)
        // 组件依赖的state
        this.state = {
          storeState: null,
        }
      }
      componentDidMount() {
        // 获取store 观察者
        const store = this.context
        this.unsubscibe = store.subscribe(() => {
          this.setState({
            storeState: mapStateToProps(store.getState()),
          })
        })
      }
      componentWillUnmount() {
        this.unsubscibe()
      }
      render() {
        return (
          <WrapperComponent
            {...this.props}
            {...mapStateToProps(this.context.getState())}
            {...mapDispatchToProps(this.context.dispatch)}
          ></WrapperComponent>
        )
      }
    }
    return EnhanceComponet
  }
}
export default connect

解析

  • 在connect 的实现中,为了store数据实现共享互通,采用了创建Context通信的方式,在文件夹下额外创建了一个context文件夹,在文件夹下创建index.js文件用于context共享。
  • 通过传入两个修改方法,将组件进行高阶组件修饰然后返回。
  • 通过extends PureComponents来进行浅比较,避免无效渲染。
  • 在组件完成挂载时,将store.subscribe()用于订阅 store 的变化。当 store 中的 state 发生变化时,订阅函数会被调用。
  • 在即将卸载组件调用unsubscribe()进行注销。最后将所有的数据都挂在组件参数上。

 react-redux/context/index.js

注意:这里使用export导出,进行数据解构

import React from 'react'
export const StoreContext = React.createContext()
//使用解构的方式进行导出

Provider.js

import React, { Component } from 'react'
import { StoreContext } from './context'
class Provider extends Component {
  constructor(props) {
    super(props)
    this.store = props.store
  }
  render() {
    return (
      <StoreContext.Provider value={this.store}>
        {/* 匿名插槽 */}
        {this.props.children}
      </StoreContext.Provider>
    )
  }
}

export default Provider

注意,这里是实现provider采用的是通信组件Context的provider提供者,所以用的是value={}

再来看看之前的index.js

这里面是将导入的store作为props传入,然后共享给connect,this.porps.children也给App留位置 

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import { Provider } from './react-redux'
import App from './App'
import store from './store'
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

看看运行:可以看到效果跟之前的react-redux是一样的

 对了,把之前的异步操作加入进来;

不过首先要提到两个插件

redux中间件

利用redux中间件机制可以在实际action响应前执行其它额外的业务逻辑。

特点:自由组合,自由插拔的插件机制

使用:applyMiddleware(logger, thunk)

 store/index.js

import { createStore, combineReducers, applyMiddleware } from 'redux'
import counter from '../reducer/counter'
import thunk from 'redux-thunk'
import logger from 'redux-logger'
// 创建reducer 具体状态执行者

const store = createStore(
  combineReducers({ counter }),
  applyMiddleware(logger, thunk)
)

export default store

 将异步行为加入到reducers(counter.js)当中

如果是异步,就会以函数的形式进行dispatch

// 异步的行为
const asyncIncrement = () => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch({ type: 'INCREMENT' })
    }, 1000)
  }
}

// 处理action事件 dispatch
export const mapDispatchToProps = (dispatch) => {
  return {
    increment() {
      // dispatch({}) 表示同步操作,如果是个函数,则是异步
      dispatch({ type: 'INCREMENT' })
    },
    decrement() {
      dispatch({ type: 'DECREMENT' })
    },
    asyncIncrement() {//异步操作
      dispatch(asyncIncrement())
    },
  }
}

compontents/Counter.js

加入异步操作按钮 

class Counter extends Component {
  render() {
    const { count, increment, decrement, asyncIncrement } = this.props
    return (
      <div>
        当前数值:{count}
        <button onClick={() => increment()}>+1</button>
        <button onClick={() => decrement()}>-1</button>
        <button onClick={() => asyncIncrement()}>异步+1</button>
      </div>  
    )
  }
}

查看运行

当进行同步dispatch时,日志logger自动打印

 当进行异步dispatch时

日志很详细,甚至连时间都能打印,是不是很好用呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值