Redux快速上手之【Redux基础】

前言

  • 细阅此文章大概需要 <math xmlns="http://www.w3.org/1998/Math/MathML"> 6 分钟 \color{red}{6分钟} </math>6分钟左右
  • 本篇中讲述了:
    1. redux简述
    2. redux管理公共状态的基本流程
      1. 核心操作
      2. 派发过程
      3. 其他
    3. Redux使用详解
      1. 引入相关依赖
      2. 创建仓库
      3. 创建公共容器
      4. 跟组件基于上下文传递store
      5. 后代组件使用state
      6. 后代组件进行行为派发
  • 欢迎在评论区探讨、留言,如果认为有任何错误都还请您不吝赐教,万分感谢。希望今后能和大家共同学习、进步。
  • 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些图解而断断续续的进行修改。
  • 如果觉得这篇文章对您有帮助,还请点个赞支持一下,谢谢大家!
  • 欢迎转载,注明出处即可。

redux简述

首先,我们都知道在react项目中通常用到的复合组件通信方案包括两大类:

  1. 父子组件之间的通信:基于props属性进行通信基于Ref进行通信
  2. 祖先和后代通信:通过Context上下文进行通信

除此之外,还有一种常见的实现组件信息共享、组件通信、全局公共状态管理的方案:redux/react-redux

接下来的几篇文章,我们就来学习一下reduxredux工程化react-reduxredux和react-redux部分源码的解读以及redux中间件及处理机制相关的内容。


redux管理公共状态的基本流程

核心操作

1. 创建一个全局公共容器: 用来存储各个组件需要的公共信息。这个容器包含两部分内容:

  1. 公共状态:各个组件需要共享、通信的信息
  2. 事件池:(存放一些让组件可以更新的方法)
javascript 复制代码
const store = createStore([reducer])

2. 在组件内部,获取最新的公共状态信息。

javascript 复制代码
store.getState()

3. 创建一些可以让组件更新的方法,将其放到公共容器的事件池当中。

当公共状态一旦发生改变,就会默认立即通知事件池中的方法就会按照顺序依次执行,就会使对应的组件更新。组件只要更新,就可以从store容器中获取最新的状态进行渲染
subScribe方法的返回值是一个方法,执行这个方法可以将刚才放入事件池中的方法移除掉。

javascript 复制代码
let unsubscribe = store.subScribe(FUNC)

4. 创建公共容器的时候需要传递reducer方法 ,它就相当于是一个管理员的角色,负责修改容器中的公共状态,并执行事件池中的方法

reducer方法执行时,会接收两个参数:

  1. state(容器中现有的公共状态,若没有则使用设置的默认值)
  2. actiondispatch执行时传入的行为对象,行为对象中必须包含type属性(代表派发的行为标识),reducer方法执行时会根据type值的不同来修改不同的状态信息
    reducer方法执行的返回值: return state返回的信息将会替换store容器中的公共状态
    tips:reducer中我们不能直接在过程中对state进行修改,而是需要在return时state进行整体替换,所以就需要我们在最开始把获取的state克隆一份
javascript 复制代码
// 为reducer创建一个初始的状态值
 const initState = {
    a:100,
    b:100
  } 

  const reducer = function reducer(state=initState, action){
    switch(action.type){
      // ...根据传递的type不同,修改不同的状态信息
    }
    return state // 返回的信息将会替换store容器中所有的公共状态
  }

5. 派发任务,通知reducer方法执行,修改容器中的公共状态

我们不能直接修改容器中的状态,而是需要通过执行store.dispatch(行为对象[OBJ])派发任务,来通知reducer方法执行来从而修改容器中的公共状态,并执行事件池中的方法,更新对应的组件。

javascript 复制代码
store.dispatch({
    type:xxx,
    // ...
})

tips:想要找到reducer,就先去找store,然后找到对应的reducer,然后再根据行为标识,找到本次派发都执行了哪些逻辑,修改了哪些状态。

派发过程

  1. 第一次派发,实际上是在redux内部派发的,state没有值。 派发标识是一个随机生成的"乱码",不会和reduce中的任何一个逻辑匹配,最终会将自己设置的初始值赋给state
js 复制代码
// 首次派发是redux进行初始化的内部派发
store.dispatch({
    type:'随机生成的乱码,与我们reducer中的任何case都不匹配',
    // ...
})

// 在reducer中最终return的是我们设置的初始state
// 此时state由undefined变为initState
  1. 第二次派发,实际上是给予我们自己的逻辑进行的第一次派发。

注意在进入switch前,克隆一份state,避免在过程中直接操作state,而是在return时对state进行整体替换

其他

除此之外,我们想要在组件当中用起来,还需要context上下文的配合。

在根组件中创建上下文,导入store,将其传递到上下文中,之后在我们需要用到store的后代组件中,通过上下文获取并进行使用即哪些


Redux使用示例

引入相关依赖

在package.json中引入reduxreact-redux以及一些其他的中间件

创建仓库

一般来说,我们会在src路径下创建一个store文件夹,并在其中新建一个index.js文件来创建公共容器,从而对公共状态进行管理

创建公共容器

javascript 复制代码
// 创建公共容器
import {createStore} from 'redux'

// 为reducer创建一个初始的状态值
const initState = {
   a:0,
   b:0
} 
/*管理员:修改store中的公共状态*/
const reducer = function reducer(state=initState, action){
// state: store中的公共状态(最开始没有时,为自己设置的初始值)
// action:dispatch执行时传入的行为对象,行为对象中必须包含type属性(代表派发的行为标识)

// 为了避免在过程中对`state`进行修改,而是需要在`return时`对`state`进行整体替换,所以就需要我们在最开始把获取的state克隆一份
state={...state}

  switch(action.type){
    // ...根据传递的type不同,修改不同的状态信息
    case "adda":
        state.a++
        break;
    case "addb":
        state.b++
        break;
    default:
        break;
            
  }
  return state // 返回的信息将会替换store容器中所有的公共状态
}

/*创建公共容器*/
const store = createStore(reducer)

export default store

根组件基于上下文传递store

js 复制代码
// 在根节点引入store,基于上下文传递
import React from 'react';
import ReactDom from 'react-dom';
import App from './view/App';
/*引入redux公共容器*/
import store from './store';
/*引入上下文*/
import AppContext from './AppContext';
const root ReactDOM.createRoot(document.getElementById('root'))
root.rnder(
    <AppContext.Provider value={{store}}> // 基于上下文传递公共容器
        <App />
    </AppContext.Provider>
)

后代组件使用state

js 复制代码
/*引入上下文*/
import AppContext from './AppContext';
import Operator from './Operator';

const App = function () {
  const {store} = useContext(AppContext) // 解构出我们的公共容器
  const { a,b } = store.getState()// 获取组件中的公共状态
  
  const [render, setRender] = useState(0);
  const update = () => {
      setRender(Math.random())
      // 类组件可以直接基于this.forceUpdate()更新,操作更为方便
  }
  useEffect(()=>{
      let unsubscribe = store.subscribe(update) 
  },[])

  return <div>
      <div>总数是:{a+b}</div>
      <div>a总数是:{a}</div>
      <div>b总数是:{b}</div>
      <Operator />
  </div>;
};

后代组件进行行为派发

js 复制代码
/*引入上下文*/
import AppContext from './AppContext';

const Operator = function () {
  const {store} = useContext(AppContext) // 解构出我们的公共容器
  
   const handleAddA = () => {
        store.dispatch({type:'adda'})
  }
   const handleAddB = () => {
       store.dispatch({type:'addb'})
  }

  return <div>
      <button onclick={handleAddA}>add a</button>
      <button onclick={handleAddB}>add b</button>
      // 点击按钮时派发任务,通知reducer执行,传递action【行为对象】,action中type为行为标识,在reducer中会找到对应的case并执行
  </div>;
};

export default Operator

相关推荐
excel11 分钟前
webpack 核心编译器 十四 节
前端
excel18 分钟前
webpack 核心编译器 十三 节
前端
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰11 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪11 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪11 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy12 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom12 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom12 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom12 小时前
React与Next.js:基础知识及应用场景
前端·面试·github