likes
comments
collection
share

谈谈我自己对redux的理解

作者站长头像
站长
· 阅读数 62

Redux是一个JavaScript状态管理库,用于管理Web应用程序中复杂的UI组件的状态。它采用可预测和可维护的方式来跟踪应用程序中的所有组件的状态,并提供一种统一的方法来更新这些状态。

Redux的核心概念是store(仓库)和action(操作)。当应用程序的状态发生变化时,Redux会创建一个新的action对象,并将其发布到store中。然后,reducer函数会被调用来更新store中的状态。最后,组件通过订阅store中的action来响应状态的变化。

Redux的使用场景包括但不限于以下几个方面:

  1. 大型单页应用程序(SPA):在SPA中,每个组件都可能需要访问共享的状态,而Redux可以帮助管理这些状态,确保它们在整个应用程序中保持一致。
  2. 复杂UI组件:当一个UI组件需要依赖于其他组件的状态时,使用Redux可以确保状态的正确更新,并且不会破坏应用程序的一致性。
  3. 跨多个页面的应用程序:在多页面应用程序中,不同的页面可能需要共享相同的数据和状态。使用Redux可以确保这些状态在整个应用程序中保持一致。
  4. 需要进行大规模重构的应用程序:当应用程序需要进行大规模重构时,使用Redux可以使状态管理更加简单和可预测。

下面我们来看一下redux具体怎么用的。以下例子将在类组件,函数组件,搭配useEffect,useContext,context。

我们总共需要5个文件分别是context.js,Vote.jsx,VoteMain.jsx,VoteFooter.jsx,以及入口文件index.js。

首先,在React中,Context(上下文)是一种用于跨组件传递数据和状态的机制。它允许您在整个应用程序中共享数据和状态,而不必在每个组件中重复编写相同的代码。

Context API提供了一种创建上下文对象的方法,该对象可以在应用程序中的任何地方使用。通过将Context对象传递给子组件,您可以确保它们访问的数据和状态是一致的,并且不会污染其他组件的数据和状态。

context.js,代码如下:

import React from "react";
const ThemeContext = React.createContext();
export default ThemeContext;

祖先组件就是咱们的入口文件index.js,父组件是Vote.jsx,VoteMain.jsx和VoteFooter.jsx是子组件,祖先代码如下。

祖先组件index.jsx如下

root.render(
    <ConfigProvider locale={zhCN}>
            <Vote></Vote>
    </ConfigProvider>
)

父组件Vote.jsx代码如下:

import React,{useContext,useState,useEffect} from "react";
import VoteFooter from "./VoteFooter";
import VoteMain from "./VoteMain";
import './Vote.less'

const Vote = function Vote() {
    return(
        <div className="vote-box">
            <div className="header">
                <h2 className="title">React真是个好框架</h2>
                <span className="num">0</span>
            </div>
            <VoteMain></VoteMain>
            <VoteFooter></VoteFooter>
        </div>
    )
    
}
export default Vote;

子组件VoteMain[类组件写法]代码如下:

import React from "react"
import ThemeContext from './context'
class VoteMain extends React.Component{
    render(){
        return(
            <div className="main">
                <p>支持人数:10人</p>
                <p>反对人数:10人</p>
            </div>
        )
    }
   
}
export default VoteMain;

子组件VoteFooter代码如下:

import { Button } from "antd";
const VoteFooter = function VoteFooter() {
    return (
        <div className="footer">
            <Button type="primary">
                支持
            </Button>
            <Button type="primary" danger>反对</Button>
        </div>
    )
    
}
export default VoteFooter;

现在我想要通过祖先组件拿到redux的值,然后再传递到子组件中,我该如何操作呢?首先祖先组件要传值到后代组件,我们可以props层层传递,那么还可以通过context上下文来获取,上文我们写了context.js的代码,导出了 ThemeContext,那么我们就可以使用了,通过ThemeContext.Provider,在祖先组件中,将父组件进行包裹起来。

root.render(
    <ConfigProvider locale={zhCN}>
        <ThemeContext.Provider>
            <Vote></Vote>
        </ThemeContext.Provider>
        
    </ConfigProvider>
)

接下来就看一下redux文件具体怎么写。redux是可以用来存储公共的一些状态就相当于是个仓库一下,那么在src目录下创建store文件夹,然后创建index.js,这个就是咱们的redux文件。

对于redux的使用,分5步来,都是特定的套路

  1. 创建全局公共的容器 const store = createStore()
  2. 在组件内部获取公用的状态信息,然后渲染 store.getState()
  3. 把”让组件可以更新“的方法放在公共容器的事件池中 store.subscribe(函数)
  4. 记住,创建容器的时候要传递reducer const store = createStore(reducer)
  5. 根据type进行派发任务,通知reducer执行修改的状态

下面的这段代码是redux的整个文件,创建了容器,然后创建了reducer函数,里面就是存放的一些数据方法也就是state,action,最近进行传递reducer,最后导出。

import {createStore} from 'redux'


let initial = {
    supNum:10,
    oppNum:5
}
/** 管理员:修改STORE容器中的公共状态 */
//这边需要定义reducer函数,传参也是固定的state跟action
const reducer = function reducer(state = initial,action){
    /**
     * state:存储STORE容器中的公共状态【最开始没有的时候,赋值初始状态initial】
     
     * action:每一次基于dispatch派发的时候,传递进来的行为对象
     【要求必须具备type属性,存储派发的行为标识】📢📢📢📢📢📢很重要,通过type来进行dispatch派发
     
     
     * 为了接下来的操作中,我们操作state,不会直接修改容器中的状态
     【要等到最后return的时候,我们需要先克隆】state = {...state} 📢📢📢📢📢📢很重要
     
     
     * 接下来我们需要基于派发的行为标识,修改STORE容器中的公共状态信息
     * 
     */
    state = {...state}
    
    //这边也可以改为if....else...的写法
    switch(action.type){
        case "VOTE_SUP":
            state.supNum++
            break;
        case 'VOTE_OPP':
            state.oppNum++
            break;
        default:

        
    }   //匹配不同的操作类别或选择器,比如匹配动态或类似的操作类别或选择器,如果没有特定的匹配选择器,则应将action转换为type匹配的主题名称或选择器名称,如果匹配,则将其转换为具有相同主题名称或选择器的新状态。这些主题名称或选择器名称应在store.js中列出。 (请
    return state;
}
//
const store = createStore(reducer);
export default store;

下面在祖先文件index.jsx中获取到这个store的公共值

 <ConfigProvider locale={zhCN}>
 //这边也是固定写法,因为要获取的是store的值,所以这边的value也就是传递的是store
        <ThemeContext.Provider value={{store}}>
            <Vote></Vote>
        </ThemeContext.Provider> 
    </ConfigProvider>

现在在Vote.jsx组件中进行获取store中的状态值,因为Vote组件是用函数组件写的,所以用HOOK函数useContext

import React,{useContext,useState,useEffect} from "react";
import VoteFooter from "./VoteFooter";
import VoteMain from "./VoteMain";
import './Vote.less'

//上下文特定写法,不要纠结    一定要注意咱们现在的值都是从祖先组件中拿取的
import ThemeContext from "./context";


const Vote = function Vote() {
   //上下文特定写法,不要纠结
    const {store} = useContext(ThemeContext)
    console.log(store)
    
    这边打印看到已经拿到store的值了,是个对象
    1.  Object
1.  1.  @@observable: ƒ observable()
    1.  dispatch: ƒ dispatch(action)
    1.  getState: ƒ getState()
    1.  replaceReducer: ƒ replaceReducer(nextReducer)
    1.  subscribe: ƒ subscribe(listener)
    1.  [[Prototype]]: Object
    
    
    
    
    
    
    // 获取容器中的公共状态
    let {supNum,oppNum} =store.getState()  这边也拿到了store中的初始值{supNum: 10, oppNum: 10}
    
    

 【这边可能有人有疑问了,这是什么意思?说到底,这边的状态函数就是单纯为了让组件进行更新的一个方法,把这个update方法
 放到事件池中,让这个来促使组件的更新,只要保证每次的num值与上一次的不一样就好,这边就是这个作用,因为我们看到了
 store.subscribe()】

   // 组件第一次渲染完毕后,把让组件更新的方法,放在STORE的事件池中
    let [num,setNum] = useState(0)
    const update = ()=>{
        setNum(num+1)
    }
    useEffect(()=>{
        let unsubscribe = store.subscribe(update) 		
        return()=>{
            unsubscribe();   //在上一次组件释放的时候,把上一次放在事件池中的方法移除掉
        }

    },[num])  
    
    
    【或者还可以其他的写法,可以不用像上面那么麻烦,还是只在第一次渲染完往事件池中加入一个方法即可,
    必须保证这个方法执行,修改的状态值和之前的状态值不一样!!时间戳或者随机数,请看下面的写法,这个也是为了能够让组件更新】
    
    
     let [_,setNum] = useState(0)
    useEffect(()=>{
        store.subscribe(()=>{
            setNum(+new Date)
        })
    })

    return(
        <div className="vote-box">
            <div className="header">
                <h2 className="title">React是真尼玛的难学啊</h2>
                <span className="num">{supNum+oppNum}</span>
            </div>
            <VoteMain></VoteMain>
            <VoteFooter></VoteFooter>
        </div>
    )
    
}
export default Vote;

最后我们再看一下dispatch 的派发,这是任务方法,咱们到VoteFooter.jsx中


import React,{useContext} from "react";
import { Button } from "antd";



//同样用到上下文,一定要注意咱们现在的值都是从祖先组件中拿取的
import ThemeContext from "./context";


// 在方法中不需要获取到公共的状态信息,而是获取方法   dispatch

const VoteFooter = function VoteFooter() {

    const {store} = useContext(ThemeContext)

    return (
        <div className="footer">
            <Button type="primary"  onClick={()=>{
                store.dispatch({
                    type:'VOTE_SUP'    //这边就是通过type这个标识来获取到底要派发哪个任务了
                })
            }}>
                支持
            </Button>
            <Button type="primary" danger onClick={()=>{
                store.dispatch({
                    type:'VOTE_OPP'
                })
            }}>反对</Button>
        </div>
    )
    
}
export default VoteFooter;

写了这么多,最后总结一下具体的代码编写

  1. 创建store,规划出reducer[当中的业务也就是action这些可以以后开发中不断完善,但是最开始reducer架子要有]
  2. 在入口中,基于上下文对象,把store放入到上下文中;需要用到store的组件从上下文获取
  3. 组件中基于store,完成公共装药的获取和任务方法的派发,重点重点!!!!使用到公共状态的组件必须向store事件池中加入让组件更新的方法,只有这样才可以确保公共状态改变,组件更新

每个人对redux的理解都是大同小异,只要是有利自己理解那都是好方法!!!如果错误还请各位指出!!!

转载自:https://juejin.cn/post/7237065395941638203
评论
请登录