Redux梳理总结一文通透

第6章:redux

一、redux理解

1. 学习文档

  • 英文文档: https://redux.js.org/
  • 中文文档: http://www.redux.org.cn/
  • Github: https://github.com/reactjs/redux

2. redux是什么

  • redux是一个专门用于做状态管理的JS库(不是react插件库)。

  • 它可以用在react, angular, vue等项目中, 但基本与react配合使用。

  • 作用: 集中式管理react应用中多个组件共享的状态。

  • 安装:npm i redux

3. 什么情况下使用redux

  • 某个组件的状态,需要让其他组件可以随时拿到(共享)。

  • 一个组件需要改变另一个组件的状态(通信)。

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

4. redux工作流程

二、redux的三个核心概念

1. action

actionstore 中唯一的数据来源,一般来说,我们会通过调用 store.dispatch 将 action 传到 store,我们需要传递的 action 是一个对象,它必须要有一个 type

  • 动作的对象

  • 包含两个属性

    • type:标识属性,值为字符串,唯一,必要属性
    • data:数据类型,值类型任意,可选属性
  • 例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }

    /* 
        该文件专门为Count组件生成action对象
    */
    import { INCREMENT, DECREMENT } from "./constant";
    export const createIncrementAction = (data) => ({ type: INCREMENT, data });
    
    export const createDecrementAction = (data) => ({ type: DECREMENT, data });
    

2. reducer

在 Reducer 中,我们需要指定状态的操作类型,要做怎样的数据更新,因此这个类型是必要的。reducer 会根据 action 的指示,对 state 进行对应的操作,然后返回操作后的 state

  • 用于初始化状态加工状态

  • 根据旧的state和action, 产生新的state的纯函数。

  • 作为createStore的参数

  • 代码:

    /* 
        1.该文件用于创建一个为Count组件服务的reducer,reducer的本质是一个函数
        2.reducer会接受到两个参数,分别为之前的状态(preState),动作对象(action)
    */
    import { INCREMENT, DECREMENT } from "./constant";
    const initState = 0; // 初始化状态
    export default function countReducer(preState = initState, action) {
      //   console.log(preState);
      const { type, data } = action;
      switch (type) {
        case INCREMENT: // 加
          return preState + data;
        case DECREMENT: // 减
          return preState - data;
        default:
          return preState;
      }
    }
    

3. store

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

在生产中,我们需要在 src 目录下的 redux 文件夹中新增一个 store.js 文件,在这个文件中,创建一个 store 对象,并暴露它

  • 将state、action、reducer联系在一起的对象

  • 如何得到此对象?

    import {createStore} from 'redux'
    import reducer from './reducers'
    const store = createStore(reducer)
    
  • 此对象的功能?

    • getState(): 得到state

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

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

  • 代码:

    /* 
        该文件专门用于暴露一个store对象,整个应用只有一个store对象
    */
    
    // 引入createStore
    import { legacy_createStore } from "redux";
    // 引入为Count组件服务的reducer
    import countReducer from "./count_reducer";
    
    // 暴露store
    export default legacy_createStore(countReducer);
    

三、redux核心API

1. createstore(reducer)

作用:创建包含指定reducer的store对象

2. store对象

  • 作用: redux库最核心的管理对象

  • 它内部维护着:

    • state
    • reducer
  • 核心方法:

    • getState():获取当前时刻的 store
    • dispatch(action):通过 store 中的 dispatch 方法来派生一个 action 对象给 store
    • subscribe(listener)subscribe方法,这个方法可以帮助我们订阅 store 的改变,只要 store发生改变,这个方法的回调就会执行
  • 具体编码:

    • store.getState()

    • store.dispatch({type:'INCREMENT', number})

    • store.subscribe(render)

      store.subscribe(() => {
          ReactDOM.render( < App /> , document.getElementById('root'))
      })
      

3. applyMiddleware()

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

import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))

4. combineReducers()

作用:合并多个reducer函数

5. redux异步编程

  • redux默认是不能进行异步处理的,

  • 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)

  • 使用异步中间件

    npm install --save redux-thunk

    import thunk from 'redux-thunk'
    export default createStore(countReducer, applyMiddleware(thunk))
    
  • 代码:

    export const createIncrementAsyncAction = (data, time) => {
        // 无需引入 store ,在调用的时候是由 store 调用的
        return (dispatch) => {
            setTimeout(() => {
                dispatch(createIncrementAction(data))
            }, time)
        }
    }
    
  • 注意:异步 action 不是必须要写的,完全可以自己等待异步任务的结果后再去分发同步action

  • 采用 react-thunk 能让异步代码像同步代码一样执行,在 redux 中我们也是可以实现异步的,但是这样我们的代码中会有很多异步的细节,这不是我们想看到的,利用 react-thunk 之类的库,就能让我们只关心我们的业务

6. Redux 三大原则

理解好 Redux 有助于我们更好的理解接下来的 React -Redux

  • 第一个原则

    • 单向数据流:整个 Redux 中,数据流向是单向的

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

  • 第二个原则

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

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

四、react-redux

在前面我们学习了 Redux ,我们在写案例的时候,也发现了它存在着一些问题,例如组件状态无法公用,每一个状态组件都需要通过订阅来监视,状态更新会影响到全部组件更新,面对着这些问题,React 官方在 redux 基础上提出了 React-Redux 库,专门用来简化react应用中使用redux

安装:npm i react-redux

1. 容器组件和 UI 组件

  • UI组件

    • 只负责 UI 的呈现,不带有任何业务逻辑

    • 通过props接收数据(一般数据和函数)

    • 不使用任何 Redux 的 API

    • 一般保存在components文件夹下

  • 容器组件

    • 负责管理数据和业务逻辑,不负责UI的呈现

    • 使用 Redux 的 API

    • 一般保存在containers文件夹下
      在这里插入图片描述
      要实现容器组件和 UI 组件的连接,我们需要通过 connect 来实现

2. Provider

由于我们的状态可能会被很多组件使用,所以 React-Redux 给我们提供了一个 Provider 组件,可以全局注入 redux 中的 store ,只需要把 Provider 注册在根部组件即可

在 src 目录下的 index.js 文件中,引入 Provider ,直接用 Provider 标签包裹 App 组件,将 store 写在 Provider 中即可。这样我们在 App.jsx 文件中,组件无需手写指定 store ,即可使用 store

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

3. connect

在前面我们看到的 react-redux 原理图时,我们会发现容器组件需要给 UI 组件传递状态和方法,并且是通过 props 来传递,看起来很简单。但是,我们会发现容器组件中似乎没有我们平常传递 props 的情形。

因为UI组件是通过**connect()(UI组件)**这样的形式连接。这时候就需要继续研究一下容器组件中的唯一一个函数 connect

  • connect 方法是一个连接器,用于连接容器组件和 UI 组件的高阶函数,它第一次执行时,接收4个参数,这些参数都是可选的,第一次执行时的四个参数:mapStateToPropsmapDispatchToPropsmergePropsoptions,它执行的执行的结果还是一个函数,第二次执行接收一个 UI 组件参数

  • mapStateToProps:函数

    建立了UI 组件和容器组件间的状态传递

    • 代码:const mapStateToProps = state => ({ count: state })

    • 它接收 state 作为参数,并且返回一个对象,这个对象标识着 UI 组件的同名参数

    • 返回的对象中的 key 就作为传递给 UI 组件 props 的 key,value 就作为 props 的 value

    • UI 组件中直接通过 props 来读取 count

    <h1>当前求和为:{this.props.count}</h1>

  • mapDispatchToProps:函数或者对象

    • connect 接受的第二个参数是 mapDispatchToProps,它是用于建立 UI 组件的参数到 store.dispacth 方法的映射

    • 我们可以把参数写成对象形式,在这里面定义 action 执行的方法,例如 jia 执行什么函数,jian 执行什么函数?

    • 我们都可以在这个参数中定义,如下定义了几个方法对应的操作函数

      {
          jia: createIncrementAction,
          jian: createDecrementAction,
          jiaAsync: createIncrementAsyncAction
      }
      
    • 自动调用dispatch

      • 似乎少了点什么,我们在这里调用了函数,创建了 action 对象,但是好像 store 并没有执行 dispatch ,那是不是断了呢?执行不了呢?

      • 其实这里 react-redux 已经帮我们做了优化,当调用 actionCreator 的时候,会立即发送 actionstore 而不用手动的 dispatch

  • connect(mapStateToProps, mapDispatchToProps)(CountUI);

4. react-redux的优化

  • 容器组件和UI组件整合一个文件

  • 无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。

  • 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。即不用写一下代码:

    // 监测redux中状态的改变,如果redux的状态发生了改变,那么重新渲染App组件
    store.subscribe(() => {
      ReactDOM.render(<App />, document.getElementById("root"));
    });
    
  • mapDispatchToProps也可以简单的写成一个对象

  • 一个组件要和redux“打交道”要经过哪几步?

    • 定义好UI组件—不暴露

    • 引入connect生成一个容器组件,并暴露,写法如下:

      connect(
      	state => ({ key : value }), //映射状态
      	{ key : xxxxxAction } //映射操作状态的方法
      )(UI组件)
      
    • 在UI组件中通过this.props.xxxxxxx读取和操作状态

5. react-redux数据共享

  • 定义一个Pserson组件,和Count组件通过redux共享数据。

  • 为Person组件编写:reducer、action,配置constant常量。

  • 重点:Person的reducer和Count的Reducer要使用combineReducers进行合并, 合并后的总状态是一个对象!!!

    // 暴露store
    export default legacy_createStore(
      combineReducers({ count: countReducer, persons: personReducer }),
      applyMiddleware(thunk)
    );
    
  • 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。

    export default connect(
      (state) => ({ personList: state.persons, count: state.count }),
      {
        addPerson: createAddPersonAction,
      }
    )(Person);
    
  • 优化:reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

6. react-redux开发者工具的使用

  • 下载Chrome浏览器插件
    地址:Redux-DevTools 浏览器调试工具

  • 在项目中安装redux-devtools-extension

    npm i redux-devtools-extension

  • 在store中进行配置

    // 引入redux-devtools-extension
    import { composeWithDevTools } from "redux-devtools-extension";
    
    // 暴露store
    export default legacy_createStore(
      combineReducers({ count: countReducer, persons: personReducer }),
      composeWithDevTools(applyMiddleware(thunk))
    );
    
  • 成功配置如下
    在这里插入图片描述

7. 纯函数

  • 如果函数的调用参数相同, 则永远返回相同的结果. 它不依赖于程序执行期间函数外部任何状态或数据的变化, 只依赖于传入的参数

  • 必须遵守以下一些约束

    • 不得改写参数数据

    • 不会产生任何副作用,例如网络请求,输入和输出设备

    • 不能调用Date.now()或者Math.random()等不纯的方法

  • redux的reducer函数必须是一个纯函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘎嘎油

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值