ReactReact全家桶Redux

Posted 前端More

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReactReact全家桶Redux相关的知识,希望对你有一定的参考价值。

文章目录

1 Redux概述

中文文档: http://www.redux.org.cn/

Github: https://github.com/reactjs/redux

1.1 Redux简介

Redux是一个专门用于做状态管理的JS库(不是react插件库)。它专门为React.js这门框架设计,但并不是只能用于react,可以用于任何界面库。 它的作用是集中式管理react应用中多个组件共享的状态。

安装:

npm i redux

使用场景

  • 某个组件的状态,需要让其他组件可以随时拿到(共享)
  • 一个组件需要改变另一个组件的状态(通信)

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

使用Redux的原因:

React仅仅是一个视图层的框架,解决的是数据与模板的渲染问题,但是并没有提供数据的状态管理方案,这在大型项目中是一个非常大的痛点 。

凡是复杂一些的应用都会有下图左边组件树的结构,非父子组件传递数据需要层层传递,如果项目稍微复杂,即使可以开发,但也变得不可维护。因此Redux应运而生。让Redux统一管理数据,任何组件对数据的操作,都交给redux管理,这样就大大提高了开发和维护的效率。

Redux用与不用的区别

1.2 Redux工作流程

首先React组件会在 Redux 中派发一个 action 方法,通过调用 store.dispatch 方法,将 action 对象派发给 store ,当 store 接收到 action 对象时,会将先前的 state 与传来的 action 一同发送给 reducerreducer 在接收到数据后,进行数据的更改,返回一个新的状态给 store ,最后由 store 更改 state

2 Redux三大核心概念

2.1 Action

action 是把数据从应用传到 store 的有效载荷,是 store 中唯一的数据来源,一般我们会通过调用 store.dispatch() 将 action 传到 store

我们需要传递的 action 是一个动作对象,它包含2个属性:

  • type:标识属性,值为字符串, 唯一, 必要属性
  • data:数据属性,,值类型任意, 可选属性

例如,这里我们暴露了一个用于返回一个 action 对象的方法,我们调用它时,会返回一个 action 对象

export const createIncrementAction = data => (
    type: INCREMENT,
    data
)

2.2 Reducer

reducer 中,我们需要指定状态的操作类型,要做怎样的数据更新,reducer还会根据 action 的指示,对 state 进行对应的操作,然后返回操作后的 state 。总结:reducer用于初始化状态及加工状态( 根据 action 更新 state )

如下,我们对接收的 action 中传来的 type 进行判断,更改数据,返回新的状态

export default function countReducer(preState = initState, action) 
    const  type,data  = action;
    switch (type) 
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            return preState
    

2.3 Store

前面我们了解了action来描述“发生了什么”,以及使用 reducer来根据 action 更新 state 的用法。 而store就是将stateactionreducer联系在一起的对象

store 是 Redux 的核心,可以理解为是 Redux 的数据中台,我们可以将任何我们想要存放的数据放在store 中,在我们需要使用这些数据时,我们可以从中取出相应的数据。

Store 的作用如下

  • 维持应用的 state;

  • getState()(https://www.redux.org.cn/docs/api/Store.html#getState):获取 state

  • dispatch(action):分发action,触发reducer调用,产生新的state

  • subscribe(listener): 注册监听,当产生了新的state时, 自动调用

  • 具体编码:

    store.getState()
    store.dispatch(type:'INCREMENT', number)
    store.subscribe(render)
    

3 Redux API

3.1 createStore()

createStore():创建包含指定reducerstore对象

3.2 applyMiddleware()

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

3.3 combineReducers()

combineReducers():合并多个reducer函数

4 使用redux编写应用

效果图

4.1 代码实现

redux/store.js :该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore,专门用于创建redux中最为核心的store对象
import  createStore  from "redux";
// 引入为Count组件服务的reducer
import countReducer from "./count_reducer.js";
// 暴露store
export default createStore(countReducer);

redux/count_reducer.js:该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)

import INCREMENT,DECREMENT from './constant'

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action)
  // console.log(preState);
  //从action对象中获取:type、data
  const type,data = action
  //根据type决定如何加工数据
  switch (type) 
    case INCREMENT: //如果是加
      return preState + data
    case DECREMENT: //若果是减
      return preState - data
    default:
      return preState
  

redux/count_action.js:该文件专门为Count组件生成action对象

import INCREMENT,DECREMENT from './constant'
export const createIncrementAction = data => (type:INCREMENT,data)
export const createDecrementAction = data => (type:DECREMENT,data)

redux/constant.js:该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错

//将这两个单词写在 `constant` 文件中,并对外暴露,当我们需要使用时,我们可以引入这个文件,并直接使用它的名称即可,直接使用 `INCREMENT` 即可
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

Count/index.jsx

import React,  Component  from 'react'
//引入store,用于获取redux中保存状态
import store from '../../redux/store'
//引入actionCreator,专门用于创建action对象
import createIncrementAction,createDecrementAction from '../../redux/count_action'

export default class Count extends Component 

  /* componentDidMount()
    //检测redux中状态的变化,只要变化,就调用render
    store.subscribe(()=>
      this.setState()
    )
   */

  //加法
  increment = ()=>
    const value = this.selectNumber
    store.dispatch(createIncrementAction(value*1))
  
  //减法
  decrement = ()=>
    const value = this.selectNumber
    store.dispatch(createDecrementAction(value*1))
  
  //奇数再加
  incrementIfOdd = ()=>
    const value = this.selectNumber
    const count = store.getState()
    if(count % 2 !== 0)
      store.dispatch(createIncrementAction(value*1))
    
  
  //异步加
  incrementAsync = ()=>
    const value = this.selectNumber
    setTimeout(()=>
      store.dispatch(createIncrementAction(value*1))
    ,500)
  

  render() 
    return (
      <div>
        <h1>当前求和为:store.getState()</h1>
        <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>
    )
  

index.js

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

ReactDOM.render(<App/>,document.getElementById('root'))

// 监测redux中状态的改变,如redux的状态发生了改变,那么重新渲染App组件
store.subscribe(()=>
    ReactDOM.render(<App/>,document.getElementById('root'))
)

4.2 总结

store.js

  • 引入redux中的createStore函数,创建一个store
  • createStore调用时要传入一个为其服务的reducer
  • 记得暴露store对象

count_reducer.js

  • reducer的本质是一个函数,接收:preState,action,返回加工后的状态
  • reducer有两个作用:初始化状态,加工状态
  • reducer被第一次调用时,是store自动触发的,传递的preState是undefined, 传递的action是:type:'@@REDUX/INIT_a.2.b.4

在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>

备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

5 Redux 三大原则

原则一单向数据流,整个 Redux 中,数据流向是单向的

UI 组件 —> action —> store —> reducer —> store

原则二state 只读,在 Redux 中不能通过直接改变 state ,来控制状态的改变,如果想要改变 state ,则需要触发一次 action。通过 action 执行 reducer

原则三纯函数执行,每一个reducer 都是一个纯函数,不会有任何副作用,返回是一个新的 state,state 改变会触发 store 中的 subscribe

6 redux-thunk异步编程

redux默认是不能进行异步处理的,某些时候应用中需要在redux中执行异步任务(ajax, 定时器),那么就需要使用异步中间件redux-thunk

6.1 redux-thunk的使用

redux-thunk的安装:

npm install redux-thunk

count_action.js:该模块专门为Count组件生成action对象

// 异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
import INCREMENT,DECREMENT from './constant'

//同步action,就是指action的值为Object类型的一般对象
export const createIncrementAction = data => (type:INCREMENT,data)
export const createDecrementAction = data => (type:DECREMENT,data)

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的
export const createIncrementAsyncAction = (data,time) => 
    return (dispatch)=>
        setTimeout(()=>
            dispatch(createIncrementAction(data))
        ,time)
    

store.js:该模块专门用于暴露一个store对象,整个应用只有一个store对象

//引入createStore,专门用于创建redux中最为核心的store对象
import 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))

6.2 总结

明确:延迟的动作不想交给组件自身,想交给action

何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。

具体编码

  1. npm install redux-thunk,并配置在store中
  2. 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
  3. 异步任务有结果后,分发一个同步的action去真正操作数据。

备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

7 react-redux

react-redux:一个React插件库,专门用来简化React应用中使用redux

7.1 react-redux中组件分类

UI组件

  • 只负责 UI 的呈现,不带有任何业务逻辑
  • 通过props接收数据(一般数据和函数)
  • 不使用任何 Redux 的 API
  • 一般保存在components文件夹下

容器组件

  • 负责管理数据和业务逻辑,不负责UI的呈现
  • 使用 Redux 的 API
  • 一般保存在containers文件夹下

react-redux模型图

7.2 react-redux相关API

  • Provider:让所有组件都可以得到state数据

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

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

  • mapDispatchToProps:将分发action的函数转换为UI组件的标签属性

7.3 基本使用

安装react-redux

npm install react-redux

components/Count/index.jsx:UI组件

import React,  Component  from 'react'

export default class Count extends Component 
  //加法
  increment = ()=>
    const value = this.selectNumber
    this.props.jia(value*1)
  
  //减法
  decrement = ()=>
    const value = this.selectNumber
    this.props.jian(value*1)
  

  //奇数再加
  incrementIfOdd = ()=>
    const value = this.selectNumber
    if(this.props.count % 2 !== 0)
      this.props.jia(value*1)
    
  
  //异步加
  incrementAsync = ()=>
    const value = this.selectNumber
    this.props.jiaAsync(value*1,500)
  

  render() 
    //console.log('UI组件接收到的props是',this.props);
    return (
      <div>
        <h1>当前求和为:this.props.count</h1>
        <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>
    )
  

containers/Count/index.js:容器组件

//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import 
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction
 from '../../redux/count_action'

//引入connect用于连接UI组件与redux
import connect from 'react-redux'

/* 
  1.mapStateToProps函数返回的是一个对象;
  2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
  3.mapStateToProps用于传递状态
*/
function mapStateToProps(state)
  return count:state


/* 
  1.mapDispatchToProps函数返回的是一个对象;
  2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
  3.mapDispatchToProps用于传递操作状态的方法
*/
function mapDispatchToProps(dispatch)
  return 
    jia:number => dispatch(createIncrementAction(number)),
    jian:number => dispatch(createDecrementAction(number)),
    jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
  


//使用connect()()创建并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

App.jsx:给容器组件传递store

import React,  Component  from 'react'
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component 
  render() 
    return (
      <div>
        /* 给容器组件传递store */
        <Count store=store />
      </div>
    )
  

7.4 总结

明确两个概念

  • UI组件:不能使用任何 redux 的api,只负责页面的呈现、交互等。
  • 容器组件:负责和 redux 通信,将结果交给UI组件。

如何创建一个容器组件:靠 react-redux 的 connect 函数

  • connect(mapStateToProps,mapDispatchToProps)(UI组件)
  • mapStateToProps: 映射状态,返回值是一个对象
  • mapDispatchToProps: 映射操作状态的方法,返回值是一个对象

备注

  • 容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
  • mapDispatchToProps,也可以是一个对象

7.5 代码优化

优化1 :简写mapDispatchToProps

//使用connect()()创建并暴露一个Count的容器组件
export default connect(
  state => (count:state),

  //mapDispatchToProps的一般写法
  /* dispatch => (
    jia:number => dispatch(createIncrementAction(number)),
    jian:number => dispatch(createDecrementAction(number)),
    jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
  ) */

  //mapDispatchToProps的简写
  
    jia:createIncrementAction,
    jian:createDecrementAction,
    jiaAsync:createIncrementAsyncAction,
  
)(CountUI)

优化2: Provider

容器组件可以检测redux中的状态改变,并渲染页面,所以不需要在index.js中检测了,不要在App.jsx中给子组件传递store了

// 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 store=store>
        <App/>
    </Provider>,
    document.getElementById('root')
)

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

每个组件两个文件夹太麻烦了,直接整合在一起就好了

7.6 数据共享版

// containers/Count/index.jsx
import React,  Component  from 'react'
//引入action
import 
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction
 from '../../redux/count_action'
//引入connect用于连接UI组件与redux
import connect from 'react-redux'

//定义UI组件
class Count ex

以上是关于ReactReact全家桶Redux的主要内容,如果未能解决你的问题,请参考以下文章

ReactReact全家桶Redux

ReactReact全家桶React脚手架

ReactReact全家桶ahooks

ReactReact全家桶React Router 6

ReactReact全家桶React Hooks

ReactReact全家桶React Hooks