React状态管理Redux原理与介绍

 更新时间:2022年08月31日 15:06:57   作者:小绵杨Yancy  
redux是redux官方react绑定库,能够使react组件从redux store中读取数据,并且向store分发actions以此来更新数据,这篇文章主要介绍了react-redux的设置,需要的朋友可以参考下

一、Redux

和vuex一样,redux的出现是为了管理web应用的公共状态。

这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

二、Redux的组成

2.1 store

store 就是保存数据的地方,整个应用只能有一个 store,可以理解为一个存储数据的仓库。

redux 提供 createStore 这个函数,用来创建一个store 以存放整个应用的 state:

import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], [enhancer]);

可以看到,createStore 接受 reducer初始 state(可选)和增强器(可选)作为参数,返回一个新的 store 对象.

2.2 state

state就是store 对象包含所有数据,如果要获取当前时刻的 state,可以通过 store.getState() 方法拿到:

import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], [enhancer]);
const state = store.getState();

2.3 action

  • state 的变化,会导致视图的变化。但是,用户接触不到 state,只能接触到视图。所以,state 的变化必须是由视图发起的。
  • action 就是视图发出的通知,通知store此时的 state 应该要发生变化了。
  • action 是一个对象。其中的 type属性是必须的,表示 action 的名称。其他属性可以自由设置,社区有一个规范可以参考:
const action = {
  type: 'ADD_TODO',
  payload: 'Learn Redux' // 可选属性 可自定义名称
};

所以action可以理解为视图层向store发送的一个命令(通知),它包含了需要执行的事件(type属性)以及传递的数据(自定义属性)。

2.4 reducer

  • store 收到 action 以后,必须给出一个新的 state,这样视图才会进行更新。state 的计算(更新)过程则是通过reducer 实现。
  • reducer 是一个函数,它接受 action 和当前 state 作为参数,返回一个新的 state:
const reducer = function (state = initState, action) {
  // ...
  return new_state;
};

创建store时,第一个参数就是reducer:

const store = createStore(reducer);

那么如何向store发送action呢?

store.dispatch({
  type: 'ADD_TODO',
  payload: 'Learn Redux'
});

store对象拥有dispath方法发送action,参数就是需要传递的action对象,然后reducer接收到action并处理,返回新的state,视图自动更新。

三、三大原则

Redux 可以用这三个基本原则来描述:

3.1 单一数据源

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

console.log(store.getState())
/* 输出
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
*/

3.2 State只读

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

这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。因为所有的修改都被集中化处理,且严格按照一个接一个的顺序执行,因此不用担心竞态条件(race condition)的出现。 Action 就是普通对象而已,因此它们可以被日志打印、序列化、储存、后期调试或测试时回放出来。

store.dispatch({
  type: 'COMPLETE_TODO',
  index: 1
})
store.dispatch({
  type: 'SET_VISIBILITY_FILTER',
  filter: 'SHOW_COMPLETED'
})

所以返回新的state时不能直接修改参数state,而是在不修改参数state的基础上返回新的state。

例如完成一个新增todo的功能:

case 'ADD_TODO':
      return state.push({
          text: action.text,
          completed: false
        })

这样是不会生效的,应为这样直接修改了state的值,正确的做法应该是:

 case 'ADD_TODO':
	    return [
	      ...state,
	      {
	        text: action.text,
	        completed: false
	      }
	    ]

这里使用了扩展运算符(…)将数组展开然后和新增的todo合并,对象同样可以使用扩展运算符达到新增属性的目的。

3.3 使用纯函数修改State

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

Reducer 只是一些纯函数,它接收先前的 stateaction,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器。

function visibilityFilter(state = 'SHOW_ALL', action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}
function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: true
          })
        }
        return todo
      })
    default:
      return state
  }
}
import { combineReducers, createStore } from 'redux'
//合并reducer
let reducer = combineReducers({ visibilityFilter, todos })
//利用reducer创建store
let store = createStore(reducer)

四、基于Redux的TodoList

效果:

todes的reducer:

const initTodos = [
  {
    id: 1,
    text: "睡觉😴",
    completed: false,
  },
  {
    id: 2,
    text: "吃饭😋",
    completed: false,
  },
  {
    id: 3,
    text: "打豆豆😜",
    completed: true,
  },
];
let nextTodoID = 4;
const todos = (state = initTodos, action) => {
  switch (action.type) {
    case "ADD_TODO":
      return [
        ...state,
        {
          id: nextTodoID++,
          text: action.text,
          completed: false,
        },
      ];
    case "TOGGLE_TODO":
      return state.map((todo) =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    default:
      return state;
  }
};
export default todos;

filter的reducer:

const visibilityFilter = (state = "SHOW_ALL", action) => {
  switch (action.type) {
    case "SET_VISIBILITY_FILTER":
      return action.filter;
    default:
      return state;
  }
};
export default visibilityFilter;

详细代码地址:

https://github.com/YancyZhang30/react-redux-todos.git

五、react-redux

redux 并不是react专有的,其本身是一个可以结合 react,vue,angular 甚至是原生 javaScript 应用使用的状态库。

react-redux是react官方提供了 react的redux适配库(这个库是可以选用的,也可以只用redux),使得我们能够更好地在react应用中使用redux来进行全局状态管理,使用react-redux主要是为了解决组件每次使用store中的数据时都必须先使用store.getState()来获取state,然后必须使用store.subscribe()进行订阅的问题。

react-redux 将所有组件分成 UI 组件和容器组件两大类:

1、 UI 组件只负责 UI 的呈现,不含有状态(this.state),所有数据都由 this.props 提供,且不使用任何 redux 的 API。

2、容器组件负责管理数据和业务逻辑,含有状态(this.state),可使用 redux 的 API。

5.1 connect方法

react-redux 提供了 connect 方法,用于将 UI 组件生成容器组件,所以如果组件想要使用store中的state,就必须先使用connect方法与store进行连接:

import {connect} from 'react-redux'
const Counter = (props) => {
    return (
        <div>
            <p>计数器: {props.num}</p>
            <div>
                <button onClick={props.increatement}>加</button> | <button onClick={props.decreate}>减</button><br/>
            </div>
        </div>
    )
}
//读取数据
const mapStateToProps=(state)=>{
    return{
        num:state
    }
}
//进行触发action
const mapDispathToProps=(dispatch)=>{
    return {
        increatement:()=>{dispatch(
            {
                type:"inCreateNum",
                num:10
            }
        )},
        decreate:()=>{dispatch(
            {
                type:"descment",
                num:10
            }
        )}
    }
}
// connect将组件与store连接。connect里的参数顺序不能颠倒
export default connect(mapStateToProps,mapDispathToProps)(Counter)

connect(mapStateToProps,mapDispathToProps)(Counter)中:

  • mapStateToProps:mapStateToProps 是一个函数,它的作用就是建立一个从 state对象(外部)到 UI 组件 props对象的映射关系。该函数会订阅 整个应用的 store,每当 state 更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。还可以使用第二个参数(可选),代表容器组件的 props 对象
  • mapDispathToProps:mapDispatchToProps 是 connect 函数的第二个参数,用来建立 UI 组件的参数到 store.dispatch 方法的映射。
  • Counter:需要变成容器组件的UI组件,也就是需要连接store的组件。

5.2 Provider组件

  • 使用 connect 方法生成容器组件以后,需要让容器组件拿到 state 对象,才能生成 UI 组件 的参数。
  • react-redux提供了 Provider 组件,可以让容器组件拿到 state,注意只有被Provider组件包含的组件才能拿到state。

main.jsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import Counter from "./Counter";
import ShowCounter from "./ShowCounter";
import reducer from "./store/counter";
import { legacy_createStore as createStore } from "redux";
const store = createStore(reducer);
ReactDOM.createRoot(document.getElementById('root')).render(
    <Provider store={store}>
        <React.StrictMode>
            <Counter></Counter>
            <ShowCounter></ShowCounter>
        </React.StrictMode>
    </Provider>
)

此时Counter组件和ShowCounter组件都可以拿到state。

ShowCounter.jsx:

import React from "react";
import {connect} from "react-redux";
const ShowCounter = (props) => {
    return (
        <div>
            <p>来自计数器的数据:<span style={{color: 'red'}}>{props.num}</span></p>
        </div>
    )
}
//读取数据
const mapStateToProps=(state)=>{
    return{
        num:state
    }
}
// connect将组件与store连接。connect里的参数顺序不能颠倒
export default connect(mapStateToProps)(ShowCounter)

由于ShowCounter组件并不需要修改store,所以mapDispathToProps参数可以直接省略了。

效果:

到此这篇关于React状态管理Redux原理与介绍的文章就介绍到这了,更多相关React Redux内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 搭建React Native热更新平台的详细过程

    搭建React Native热更新平台的详细过程

    这篇文章主要介绍了搭建React Native热更新平台,要实现 React Native 热更新功能,有四种思路 Code Push、Pushy、Expo 和自研,感兴趣的朋友一起通过本文学习吧
    2022-05-05
  • React实现antdM的级联菜单实例

    React实现antdM的级联菜单实例

    这篇文章主要为大家介绍了React实现antdM的级联菜单实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • React 项目中动态设置环境变量

    React 项目中动态设置环境变量

    本文主要介绍了React 项目中动态设置环境变量,本文将介绍两种常用的方法,使用 dotenv 库和通过命令行参数传递环境变量,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • React路由的history对象的插件history的使用解读

    React路由的history对象的插件history的使用解读

    这篇文章主要介绍了React路由的history对象的插件history的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • 详解React Native项目中启用Hermes引擎

    详解React Native项目中启用Hermes引擎

    这篇文章主要为大家介绍了React Native项目中启用Hermes引擎实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • React路由参数传递与嵌套路由的实现详细讲解

    React路由参数传递与嵌套路由的实现详细讲解

    这篇文章主要介绍了React路由参数传递与嵌套路由的实现,嵌套路由原则是可以无限嵌套,但是必须要让使用二级路由的一级路由匹配到,否则不显示,需要的朋友可以参考一下
    2022-09-09
  • react开发教程之React 组件之间的通信方式

    react开发教程之React 组件之间的通信方式

    本篇文章主要介绍了react开发教程之React组件通信详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • React通过hook实现封装表格常用功能

    React通过hook实现封装表格常用功能

    这篇文章主要为大家详细介绍了React通过hook封装表格常用功能的使用,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考下
    2023-12-12
  • React源码state计算流程和优先级实例解析

    React源码state计算流程和优先级实例解析

    这篇文章主要为大家介绍了React源码state计算流程和优先级实例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 详细聊聊React源码中的位运算技巧

    详细聊聊React源码中的位运算技巧

    众所周知在React中,主要用到3种位运算符 —— 按位与、按位或、按位非,下面这篇文章主要给大家介绍了关于React源码中的位运算技巧的相关资料,需要的朋友可以参考下
    2021-10-10

最新评论