• Redux的基本使用过程详解


    Redux的使用过程

    Redux测试项目的搭建

    1.创建一个新的项目文件夹:learn-redux

    # 执行初始化操作 npm init -yyarn init -y

    # 安装redux:npm install redux --saveyarn add redux

    2.创建src目录,在src目录下创建一个store文件夹, 并且在该文件夹下创建index.js文件

    3.可以修改package.json用于执行index.js, 也可以不配置, 直接使用node命令运行

    "scripts": {
      "start": "node src/index.js"
    }
    

    Redux的基本使用步骤

    1.创建一个对象,作为我们要保存的状态state:

    // 由于测试项目在node环境下, 因此使用require方式导入
    const { createStore } = require("redux")
    
    // 创建的要存储的state: initialState
    const initialState = {
      name: "chenyq",
      age: 18
    }
    

    2.创建Store来存储这个state

    由于创建的state是不能直接放到创建的store中的, 需要通过reducer将数据添加到store中, 因此创建store时必须创建reducer;

    reducer函数的返回值, 会作为store之后存储的state

    // 定义reducer, 将要存储的state作为返回值返回
    function reducer() {
      return initialState
    }
    
    // 创建的store, 内部会自动回调reducer, 拿到initialState
    const store = createStore(reducer)
    
    // 导出store
    module.exports = store
    

    我们可以在其他文件中通过 store.getState 来获取当前的state;

    // 导入创建的store
    const store = require("./store")
    
    // 获取store中的state
    console.log(store.getState())
    

    在这里插入图片描述

    3.通过action来修改state

    错误演示: 直接修改store

    store.getState().name = "abc"
    

    修改store中的数据不能直接修改, 必须要通过dispatch来派发action;

    通常action中都会有type属性,也可以携带其他的数据;

    // 定义一个action
    const nameAction = { type: "change_name", name: "abc" }
    // 派发action
    store.dispatch(nameAction)
    

    当然上面代码中, 也可以写为一行

    // 派发action
    store.dispatch({ type: "change_name", name: "abc" })
    

    4.修改reducer中的处理代码

    reducer接收两个参数:

    参数一: store中当前保存的state

    参数二: 本次需要更新的action

    只要调用dispatch就会重新执行reducer函数

    这里一定要记住,reducer是一个纯函数,不可以直接修改state, 后面我会讲到直接修改state带来的问题;

    // 第一次state是undefined, 因此给一个默认值将初始化数据添加到store中
    function reducer(state = initialState, action) {
    
      // 有数据更新时, 返回一个新的state
      if (action.type === "change_name") {
        return { ...state, name: action.name }
      }
    
      // 没有数据更新时, 返回之前的state
      return state
    }
    

    5.可以在派发action之前,监听store的变化:

    通过store.subscribe()函数可以监听store中的数据变化

    store.subscribe()函数的参数接收一个函数, 该函数在store数据发生更新自动回调

    const store = require("./store")
    
    // 例如: 监听数据的变化, 当store变化, 就获取最新的state
    store.subscribe(() => {
      console.log(store.getState())
    })
    
    store.dispatch({ type: "change_name", name: "abc" })
    store.dispatch({ type: "change_name", name: "aaa" })
    

    6.封装函数动态生成action

    例如上面代码中, 我们修改名称多次, 只有传入的action的name属性值不相同, 那么我们可以封装一个函数, 动态的生成action, 这也是开发中一贯的做法

    // 创建修改name的action
    const changeNameAction = (name) => ({
      type: "change_name",
      name
    })
    // 创建修改age的action
    const changeAgeAction = (num) => ({
      type: "change_age",
      num
    })
    
    // 在派发action时, 我们就可以调用函数即可获取action
    store.dispatch(changeNameAction("aaa"))
    store.dispatch(changeNameAction("bbb"))
    store.dispatch(changeNameAction("ccc"))
    
    store.dispatch(changeAgeAction(10))
    store.dispatch(changeAgeAction(20))
    store.dispatch(changeAgeAction(30))
    store.dispatch(changeAgeAction(40))
    

    Redux目录的结构划分

    如果我们将所有的逻辑代码写到一起,那么当redux变得复杂时代码就难以维护

    例如上面代码中, 我们封装的动态创建action的函数, 这种动态生成action的函数在项目中可能会有很多个, 而且在其他多个文件中也可能会使用, 所以我们最好是有一个单独的文件夹存放这些动态获取action的函数

    接下来,我会对代码进行拆分,将store、reducer、action、constants拆分成一个个文件。

    创建store/index.js文件: index文件中, 我们只需要创建store即可

    const { createStore } = require("redux")
    // 引入reducer
    const reducer = require("./reducer")
    
    // 创建的store, 内部会自动回调reducer, 拿到initialState
    const store = createStore(reducer)
    
    // 导出store
    module.exports = store
    

    创建store/reducer.js文件: 在真实项目中, reducer这个函数我们会越写越复杂, 造成我们index.js文件越来越大, 所以我们将reducer也抽离到一个单独的文件中

    const { CHANGE_NAME, CHANGE_AGE } = require("./constants")
    
    // 创建的要存储的初始化state
    const initialState = {
      name: "chenyq",
      age: 18
    }
    
    // 定义reducer, 将要存储的state作为返回值返回
    // 第一次state是undefined, 因此给一个默认值将初始化数据添加到store中
    function reducer(state = initialState, action) {
      switch(action.type) {
        case CHANGE_NAME: 
          return { ...state, name: action.name }
        case CHANGE_AGE: 
          return {...state, age: state.age + action.num}
      }
    
      // 没有数据更新时, 返回之前的state
      return state
    }
    
    module.exports = reducer
    

    创建store/constants.js文件: 将type的类型定义为常量(防止写错的情况), 这些常量最好也防止一个单独的文件中

    // store/constants.js
    
    const CHANGE_NAME = "change_name"
    const CHANGE_AGE = "change_age"
    
    module.exports = {
       CHANGE_NAME,
       CHANGE_AGE
    }
    

    创建store/actionCreators.js文件: 将封装的动态创建action的函数放在该文件中, 在需要使用的地方导入即可

    const { CHANGE_NAME, CHANGE_AGE } = require("./store/constants")
    
    // 创建修改name的action
    const changeNameAction = (name) => ({
      type: CHANGE_NAME,
      name
    })
    // 创建修改age的action
    const changeAgeAction = (num) => ({
      type: CHANGE_AGE,
      num
    })
    
    module.exports = {
      changeNameAction,
      changeAgeAction
    }
    

    最终形成如下目录结构, 这也是官方推荐的目录结构, 一个store中包含这四个文件夹

    在这里插入图片描述

    注意:node中对ES6模块化的支持, 建议使用CommonJS规范


    React的三大原则

    单一数据源

    整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store 中:

    Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护;

    单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改;

    State是只读的

    唯一修改State的方法一定是触发action,不要试图在其他地方通过任何的方式来修改State:

    这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state;

    这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题;

    使用纯函数来执行修改

    通过reducer将旧state和actions联系在一起,并且返回一个新的State:

    随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分;

    但是所有的reducer都应该是纯函数,不能产生任何的副作用;

  • 相关阅读:
    服务器模拟互联网服务
    python—进程总结
    JAVA五子棋手机网络对战游戏的设计与实现(源代码+论文)
    物 理 层
    Spring Boot + Vue的网上商城之物流系统实现
    【前端】Vue+Element UI案例:通用后台管理系统-Home组件:卡片、表格
    Elasticsearch07:ES中文分词插件(es-ik)安装部署
    python集合的应用场景
    Linux入门之使用 systemd targets
    MongoDB 6.1 及以上版本使用配置文件的方式启动报错 Unrecognized option: storage.journal.enabled
  • 原文地址:https://blog.csdn.net/m0_71485750/article/details/126732471