您现在的位置是:网站首页> 编程资料编程资料

一文详解React Redux使用方法_React_

2023-05-24 288人已围观

简介 一文详解React Redux使用方法_React_

一、理解JavaScript纯函数

1.1 纯函数的概念

纯函数的维基百科定义:

  • 在程序设计中,若一个函数符合以下条件,那么这个函数被称为纯函数
  • 此函数在相同的输入值时,需产生相同的输出
  • 函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关
  • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容

纯函数概念,总结如下:

  • 确定的输入,一定会产生确定的输出
  • 函数在执行过程中,不能产生副作用

案例(数组的两个方法):

  • slice:slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组
  • splice:splice截取数组, 会返回一个新的数组,也会对原数组进行修改

1.2 副作用概念的理解

什么是副作用?

  • 副作用(side effect)表示在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响,比如修改了全局变量,修改参数或者改变外部的存储

纯函数在执行的过程中就是不能产生这样的副作用:

  • 副作用往往是产生bug的 “温床”

1.3 纯函数在函数式编程的重要性

为什么纯函数在函数式编程中非常重要呢?

  • 可以安心的编写和安心的使用
  • 在写的时候保证了函数的纯度,只是单纯实现自己的业务逻辑即可,不需要关心传入的内容是如何获得的或者依赖其他的外部变量是否已经发生了修改
  • 在用的时候,确定的输入内容不会被任意篡改,并且自己确定的输入,一定会有确定的输出
  • React中就要求我们无论是函数还是class声明一个组件,这个组件都必须像纯函数一样,保护它们的props不被修改
  • 在下面的redux中,reducer也被要求是一个纯函数

二、Redux的核心思想

2.1 为什么需要 Redux

JavaScript开发的应用程序变得越来越复杂:

  • JavaScript需要管理的状态越来越多,越来越复杂
  • 这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等,也包括一些UI的状态,比如某些元素是否被选中,是否显示加载动效,当前分页

管理不断变化的state是非常困难的:

  • 状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View页面也有可能会引起状态的变化
  • 当应用程序复杂时,state在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常难以控制和追踪

React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理:

  • 无论是组件定义自己的state,还是组件之间的通信通过props进行传递;也包括通过Context进行数据之间的共享
  • React主要负责帮助我们管理视图,state如何维护最终还是我们自己来决定

  • Redux就是一个帮助我们管理State的容器:Redux是JavaScript的状态容器,提供了可预测的状态管理
  • Redux除了和React一起使用之外,它也可以和其他界面库一起来使用(比如Vue、小程序),并且它非常小(包括依赖在内,只有2kb)

2.2 Redux的核心概念

2.2.1 store

可以定义一些初始化的数据,通过 reducer 传入

2.2.2 action

  • store 中数据的变化,必须通过派发(dispatch)action来更新
  • action是一个普通的JavaScript对象,用来描述这次更新的type和content

2.2.3 reducer

将传入的state和action结合起来生成一个新的state

2.3 Redux的三大原则

2.3.1 单一数据源

  • 整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store 中
  • Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护
  • 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改

2.3.2 State是只读的

  • 唯一修改State的方法是触发action,不要试图在其他地方通过任何的方式来修改State
  • 这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state
  • 这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题

2.3.3 使用纯函数来执行修改

  • 通过reducer将 旧state和 actions联系在一起,并且返回一个新的State
  • 随着应用程序的复杂度增加,可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分
  • 但是所有的reducer都应该是纯函数,不能产生任何的副作用

2.4 Redux 工作流程

建议看完Redux基本使用后再来看这幅图:

三、Redux基本使用

注意:以下 3 部分代码在 node 环境下

  • 需要安装redux:npm install redux

补充:node中对ES6模块化的支持

node v13.2.0开始,对ES6模块化提供了支持:

node v13.2.0之前,需要进行如下操作:

  • 在package.json中添加属性: "type": "module"
  • 在执行命令中添加如下选项:node --experimental-modules src/index.js

node v13.2.0之后,只需要进行如下操作:

  • 在package.json中添加属性: "type": "module"
  • 注意:导入文件时,需要跟上.js后缀名

3.1 创建Store的过程

定义reducer:必须是一个纯函数,不要直接修改state

createStore 传入 reducer

const { createStore } = require('redux') // 初始化的数据 const initialState = { name: '李雷', counter: 100 } // 定义reducer函数:纯函数 // 两个参数: // 参数一:store中目前保存的state // 参数二:本次需要更新的action(dispatch传入的action) // 返回值:返回值会作为store之后存储的state function reducer(state = initialState, action) { switch (action.type) { case 'change_name': return { ...state, name: action.name } case 'add_numer': return { ...state, counter: state.counter + action.num } default: return state } } // 创建store const store = createStore(reducer) module.exports = store

3.2 dispatch派发action

  • store 通过 dispatch 来派发 action
  • 通常会有 type 属性,也可以携带其他数据
const store = require('./store') console.log(store.getState()) // { name: '李雷', counter: 100 } // 修改store中的数据:必须action const nameAction = { type: 'change_name', name: '韩梅梅' } store.dispatch(nameAction) console.log(store.getState()) // { name: '韩梅梅', counter: 100 } const nameAction2 = { type: 'change_name', name: '夏洛' } store.dispatch(nameAction2) console.log(store.getState()) // { name: '夏洛', counter: 100 } // 修改counter const counterAction = { type: 'add_numer', num: 10 } store.dispatch(counterAction) console.log(store.getState()) // { name: '夏洛', counter: 110 }

3.3 subscribe定位state

  • store.subscribe()传入一个函数能够监听数据的变化
  • store.subscribe()会返回一个函数,执行该函数取消监听
const store = require('./store') const unSubscribe = store.subscribe(() => { console.log('订阅数据的变化:', store.getState()) }) // 修改store中的数据:必须action store.dispatch({ type: 'change_name', name: '韩梅梅' }) store.dispatch({ type: 'change_name', name: '夏洛' }) // 取消订阅 unSubscribe() // 修改counter store.dispatch({ type: 'add_numer', num: 10 })

3.4 代码优化

  • 优化方向:
    • action的创建放到一个函数中
    • 抽取到actionCreators.js文件中
    • 所有的字符串常量放到constants.js文件
    • reducer函数和初始化值, 放到reducer.js文件
    • index.js中创建store和导出store

示例:

actionCreators.js

const { ADD_NUMBER, CHANGE_NAME } = require("./constants") const changeNameAction = (name) => ({ type: CHANGE_NAME, name }) const addNumberAction = (num) => ({ type: ADD_NUMBER, num }) module.exports = { changeNameAction, addNumberAction }
const ADD_NUMBER = "add_number" const CHANGE_NAME = "change_name" module.exports = { ADD_NUMBER, CHANGE_NAME }
const { CHANGE_NAME, ADD_NUMBER } = require('./constants') // 初始化的数据 const initialState = { name: '李雷', counter: 100 } function reducer(state = initialState, action) { switch (action.type) { case CHANGE_NAME: return { ...state, name: action.name } case ADD_NUMBER: return { ...state, counter: state.counter + acti
                
                

-六神源码网