Redux了解、安装及简单使用

463 阅读5分钟

redux是公共状态管理方案 官网

redux/ react-redux 是实现了组件之间的通信插件

概念

redux等于创建了一个公共容器,内部组件对需要共享的值进行存入及获取操作,达到组件通信的目的

Redux 是一个使用叫作 "actions" 的事件去管理和更新应用状态的模式和工具库。 它以集中式 Store(centralized store)的方式对整个应用中使用的状态进行集中管理,其规则确保状态只能以可预测的方式更新。Redux 是一个小型的独立 JS 库。

目的

为了更好的管理项目内越来越多的state(状态),同时也使state值变更受控制

三大原则

单一数据源

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

State 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

使用纯函数来执行修改

为了描述 action 如何改变 state tree ,你需要编写 reducers

下图是 Redux异步数据流动过程

ec13982d93afb18135f715751161f306.gif

store

所有 Redux 应用的中心都是 store 。"store" 是保存应用程序的全局 state 的容器。 使用步骤:

  1. store内的状态值不能着急修改
  2. 改状态的唯一方法是 创建一个action对象,将action对象 disapt出去
  3. 当action被dispatch后,store根据action 调用对应的reducer方法,计算出新的state
  4. store通知 订阅者(subscribers) 状态已更新,以便可以使用新数据更新 UI。

创建:

// 通过 createStore 方法创建一个新的 Redux store
// reducer 创建存储 reducer对象

import { createStore } from 'redux';
const store = createStore(reducer);

下面是store打印结果:

图片.png

store内的方法

从上图可以看到store内的几个方法:dispatch、getState、subscribe

dispatch

更新 state 的唯一方法是调用 store.dispatch() 并传入一个 action 对象

getState

store 将执行所有 reducer 函数并计算出更新后的 state,调用 getState() 可以获取新 state。

let { count } = store.getState()

subscribe

每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。你可以在回调函数里调用 getState() 来拿到当前 state。

store.subscribe(() => {
    console.log("subscribe", store.getState())
})

action

将 action 视为描述应用程序中发生了什么的事件. type: 字符串,给这个 action 一个描述性的名字,比如"todos/todoAdded"。我们通常把那个类型的字符串写成“域/事件名称”,其中第一部分是这个 action 所属的特征或类别,第二部分是发生的具体事情。

action 对象可以有其他字段,其中包含有关发生的事情的附加信息。按照惯例,我们将该信息放在名为 payload 的字段中。

const addTodoAction = {
  type: 'todos/todoAdded',
  payload: 'Buy milk'
}

reducer

reducer 是一个函数,接收当前的 state 和一个 action 对象,必要时决定如何更新状态,并返回新状态。函数签名是:(state, action) => newState你可以将 reducer 视为一个事件监听器,它根据接收到的 action(事件)类型处理事件。

const reducer = function reducer(state = initial, action) {
    state = { ...state };
    switch (action.type) {
        case 'increase':
            state.count++;
            break;
        default:
    }
    // return的内容,会整体替换STORE容器中的状态信息
    return state;
};

操作流程

第一步 创建全局公共的答器 用来存储各组件需要的公共信息

const store = createStore(reducer);

第二步 在组件内部获取公共状态信息,然后渲染

let { count } = store.getState()

第三步 监听组件内公共状态的变化,将方法放入公共容器的事件池内

store.subscribe(函数)

第四步 创建 reducer,就上面写到的

第五步 派发任务,通知reducer执行修改的状态

store.dispatch({
    type: 'increase'
});

这里先将store传入,然后获取,这里通过的是上下文React.createContext()传递数据[store]

上下文传值具体在 React 父子、跨层级 组件通信了解及使用补充了,具体可以查看

redux在运行时会在内部进行一次派发,获取数据默认值

全量代码

目录结构

图片.png

这里的 ThemeContext 是借助 上下文传递store

代码:

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
// antd汉化
import { ConfigProvider } from 'antd'
import zhCN from 'antd/locale/zh_CN';
import Demo from './views/test-redux/Demo';
import ThemeContext from './ThemeContext';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <>
    <ConfigProvider locale={zhCN}>
      <ThemeContext.Provider value={{ store }}>
        <Demo />
      </ThemeContext.Provider>
    </ConfigProvider>
  </>
);

ThemeContext.js

import React from "react";

const ThemeContext = React.createContext();

export default ThemeContext;

store/index.js

import { legacy_createStore as createStore } from 'redux';

let initial = {
    count: 0
};
const reducer = function reducer(state = initial, action) {
    console.log("action", action);
    state = { ...state };
    switch (action.type) {
        case 'increase':
            state.count++;
            break;
        case 'decrease':
            state.count--;
            break;
        default:
    }
    // return的内容,会整体替换STORE容器中的状态信息
    return state;
};

/* 创建STORE公共容器 */
const store = createStore(reducer);
export default store;

test-redux/Demo.jsx

import React, { useContext, useEffect, useState } from 'react'
import DemoMain from './DemoMain';
import DemoChange from './DemoChange';
import ThemeContext from '../../ThemeContext';

export default function Demo() {
    const { store } = useContext(ThemeContext)
    let { count } = store.getState()

    // 组件更新操作
    const [_, setNum] = useState(0)
    useEffect(() => {
        store.subscribe(() => {
            setNum(new Date())
        })
    }, [])
    return (
        <div>
            <div>总计:{count}</div>
            <DemoMain />
            <DemoChange />
        </div>
    )
}

test-redux/DemoMain.jsx

import React, { Component } from 'react'
import ThemeContext from '../../ThemeContext'

export default class DemoMain extends Component {
    static contextType = ThemeContext;

    componentDidMount() {
        // 组件更新
        let { store } = this.context
        store.subscribe(() => {
            this.forceUpdate()
        })
    }
    render() {
        let { store } = this.context
        let { count } = store.getState();
        return (
            <div style={{ margin: '20px 0' }}>
                展示内容:{count}
            </div>
        )
    }

}

test-redux/DemoChange.jsx

import React, { Component, useContext } from 'react'
import { Button } from 'antd'
import ThemeContext from '../../ThemeContext';

export default function DemoChange() {
    let { store } = useContext(ThemeContext)
    return (
        <div>
            <Button type="primary" onClick={() => {
                store.dispatch({
                    type: 'increase'
                });
            }}>增加</Button>&nbsp;
            <Button type="primary" onClick={() => {
                store.dispatch({
                    type: 'decrease'
                });
            }}>减少</Button>
        </div>
    )
    // }

}

运行结果:

图片.png

点击增加,数字加1

但是这里可以看到,加在 reducer 内的action在初始化的时候被派发了一次,说明页面在第一次加载的情况下,dispatch派发了此次给state赋初始值

redux为什么不能直接修改状态值?

官方给的是 如果想改变状态state(不能直接改),需要使用store提供的dispatch方法,去派发一个action(动作)。这个动作由Aaction Creators来创建一个对象,将动作派发出去,通知这个store进行修改,store修改不是自己进行修改,是通过Reducers进行状态改变,在Reducers改变状态以后,要通知store,因为store内可能会挂载特别的订阅,这里的订阅会通知组件去做更新。这个就是一个完整的单向数据流。

原因: 如果直接修改,公共状态的变量都分布在各个组件了,不利于维护、管理和逻辑复用 ,而且状态不利于追踪修改位置。