React笔记(八)Redux

一、安装和配置

React 官方并没有提供对应的状态机插件,因此,我们需要下载第三方的状态机插件 —— Redux。

1、下载Redux

在终端中定位到项目根目录,然后执行以下命令下载 Redux

npm i redux

2、创建配置文件

在 React 中,不会自动生成状态机的相关配置代码,因此,需要我们自己手动去创建目录以及配置文件。

我们可以在 src 的目录中创建一个 reduxstore 的目录,用来存放所有关于状态机的配置文件。然后,在该目录中创建一个 store.js 文件,作为整个状态机的入口文件。

3、配置状态机

3.1、创建store仓库对象
import {legacy_createStore as createStore} from 'redux'
const store=createStore();
3.2、保存数据到store中

createStore 方法接收一个函数作为参数,该函数的返回值,会保存到 store 仓库中

const store = createStore((state = 数据初始值) => {
    return state;
});

通过函数参数的默认值的方式,来设置仓库数据的初始值

3.3、查看仓库数据

store 仓库对象身上,提供了一个 getState() 方法,用来查看仓库中的所有数据:

console.log(store.getState());

由于目前没有任何文件中在引入状态机的配置文件,如果我们需要查看仓库中的数据,暂时需要在 index.js 中引入 /redux/store.js 文件来让其运行。

二、Redux核心概念

1、Redux工作流程

React笔记(八)Redux_第1张图片

2、Redux组成部分

2.1、state

state:状态,就是我们传递的数据

2.2、action

action是一个通知对象,里面必须有一个type属性,表示当前通知的类型,至于其他属性,你可以任意添加

可以通过store.dispatch(action对象)来更新仓库中的数据

注意:

  • 在实际开发中,更多人喜欢用action创建函数

  • 在实际开发中,大多数情况下,type会被定义成字符串常量

2.3、reducer

reducer本质是一个函数,它用来响应发送过来的actions,经过处理把state发送给store

  • 在reducer函数中,需要return返回值,这样store才能接收到数据

  • reducer函数接收两个参数,第一个参数是初始化store,第二个参数是action

React笔记(八)Redux_第2张图片

2.4、store

数据仓库,存放组件数据的地方。一个项目一般只有一个数据仓库,store可以把action和reducer联系在一起。

主要的职责

  • 维护应用的state

  • 提供getState()方法获取state

  • 提供dispatch()方法发送action

  • 通过subscribe()来注册监听

  • 通过subscribe()返回值来注销监听

三、第一个Redux程序

1、创建action

  • 在src目录下创建一个actions文件夹

  • 在该目录下创建一个index.js文件

  • action是一个通知对象,里面必须有一个type属性,这里的num属性是自定义的表示计数器每次增加的个数

const incrementAction={
    type:'increment',
    num:1
}
export {incrementAction}

2、创建reducer

  • 在src目录下创建reducers目录

  • 在该目录下创建index.js文件,用来构建reducer,注意reducer要接收两个参数

  • 第一个参数是默认状态,我们可以定义一个初始化的state,然后进行赋值

  • 在函数里面判断第二个参数action的type值是否是我们发送过的,如果是,我们可以通过return返回新的state

const counterReducer=(state=0,action)=>{
   switch(action.type){
        case 'increment':
            return state+action.num
        default:
            return state
    }
}
export {counterReducer}

3、创建store

  • 在src目录下创建一个文件夹store

  • 在该目录下创建index.js文件,用来构建store,注意createStore函数第一个参数接收的是reducer

import {legacy_createStore as createStore} from 'redux'
import {counterReducer} from '../reducers'
const store=createStore(counterReducer)
export default store

4、在组件中使用

  • 创建components文件夹,该目录下存放自定义组件

  • 在该目录下新建Counter.jsx文件

  • 将Counter.jsx引入到App.js文件中

import Counter from "./components/Counter";
function App() {
  return (
    
           
); } export default App;
  • 调用dispatch函数,更新仓库的数据

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'

export default function Counter() {
  const increment=()=>{
      store.dispatch(incrementAction)
  }
  return (
    

计数器:{store.getState()}

) }
  • 当组件加载完毕后,调用 store. subscribe注册监听,监听state数据的变化

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'

export default function Counter() {
  const [count, setCount] = useState(0);
  const increment=()=>{
      store.dispatch(incrementAction)
  }
  useEffect(()=>{
    store.subscribe(()=>{

        console.log('正在监控'+store.getState());
        setCount({})
    })
  })

  return (
    

计数器:{store.getState()}

) }

四、redux中action和reducer的优化

1、action优化

1.1、action creator

我们一般会在页面里面修改数据,即,在页面里面调用store的dispatch方法。那就意味着action对象中的num字段很大可能是动态的,即不同的页面num字段可能是不同的,这样我们就不能把action写成一个死的对象,最好是封装成一个函数,执行过后返回一个action通知对象,然后num的值通过函数的参数来决定。这样的函数我们称之为action创建函数(action creator)

const incrementAction=(num)=>{
    return{
        type:'increment',
        num:num
    }
}
export {incrementAction}
1.2、type常量

在实际开发中,type在多数情况下会被定义成常量,如下所示

  • 在src/actions目录下,新建actionTypes.jsx文件,将所有type类型定义成常量

export const INCREMENT ="increment"
  • 在action creator中引入

import {INCREMENT} from './constant'
const incrementAction=(num)=>{
    return{
        type:INCREMENT,
        num:num
    }
}
export {incrementAction}

2、reducer优化

项目中肯定不止一个数据,所以state的默认值应该是一个对象,而不是其他。而且除了默认值,每当case到一个条件,返回一个新的对象时,应该返回一个全新的对象,然后才是你要修改的数据(当然这些数据,如果是引用类型的话,应该修改其引用本身,否则界面可能不会更新,尽管数据发生变化了

import { INCREMENT } from '../actions/constant'
const counterReducer = (state = { num: 0 }, action) => {
    switch (action.type) {
        case INCREMENT:
            return {
                ...state,
                num: state.num + action.num
            }
        default:
            return state
    }
}
export { counterReducer }

优化后,在组件中调用的代码如下

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'

export default function Counter() {
  const [count, setCount] = useState(0);
  const increment=()=>{
      store.dispatch(incrementAction(2))
  }
  useEffect(()=>{
    store.subscribe(()=>{

        console.log('正在监控'+store.getState());
        setCount({})
    })
  })

  return (
    

计数器:{store.getState()}

) }

五、react-redux概述

为了方便使用,Redux 的作者封装了一个 React 专用的库 React-Redux,本文主要介绍它。

React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。

1、UI组件

UI 组件有以下几个特征。

  • 只负责 UI 的呈现,不带有任何业务逻辑

  • 没有状态(即不使用this.state这个变量)

  • 所有数据都由参数(this.props)提供

  • 不使用任何 Redux 的 API

2、容器组件

容器组件有以下几个特征。

  • 负责管理数据和业务逻辑,不负责 UI 的呈现

  • 带有内部状态

  • 使用 Redux 的 API

总之,只要记住一句话就可以了:UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。

六、react-redux基本使用

1、安装react-redux

npm i react-redux
npm i redux

2、创建action

  • 在constant.jsx中添加INCREMENT

export const INCREMENT="increment";
  • 在src/actions/index.jsx中添加incrementAction

import { INCREMENT } from "./constant";
export const incrementAction=num=>({type:INCREMENT,num})

3、创建reducer

  • 在src/reducers/index.jsx中添加counterReducer

import {INCREMENT} from '../actions/constant'
const counterReducer=(state={num:0},action)=>{
    switch(action.type){
        case INCREMENT:
            return{
                ...state,
                num:state.num+action.num
            }
        default:
            return state
    }
}
export {counterReducer}

4、创建store

在src/store/index.jsx中编写代码如下

import {legacy_createStore as createStore} from 'redux'
import {counterReducer} from '../reducers'
const store=createStore(counterReducer)
export default store

5、全局注入store仓库

  • 在index.js中导入Provider组件

import {Provider} from 'react-redux'
  • 利用provider组件将整个接口进行包裹

  • 给Provider组件设置store的属性,该属性的值就是通过createStore构建出来的store实例对象

import ReactDOM from 'react-dom/client'
import App from './App';
import {Provider} from 'react-redux'
import store from './store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    
      
    
);
  • Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。

6、组件关联仓库

由于UI组件不能使用Redux的API所以,如果在组件中如果要使用Redux就必须将UI组件变成容器类组件

React-Redux 提供connect方法,用于从 UI 组件生成容器组件。connect的意思,就是将这两种组件连起来

import React from 'react'
import { connect } from 'react-redux'

function Counter() {
  render() {
    return (
      
) } } export default connect()(Counter)

7、组件操作仓库

7.1、获取仓库数据

connect 方法接收一个回调函数作为参数:

const mapStateToProps = () => {

}
export default connect(mapStateToProps)(Counter);

该回调函数本身,又可以通过第一个参数接收到仓库中的所有数据

const mapStateToProps = (state) => {
    console.log(state); // 仓库中所有的数据
}
export default connect(mapStateToProps)(Counter);

在该回调函数中,返回一个对象,该对象会和组件的 props 进行合并。换句话说,该函数的返回值会添加到组件的 props 中:

const mapStateToProps = (state) => {
    return {
        数据名: 从 state 中获取的数据值
    }
}
export default connect(mapStateToProps)(Counter);

处理完成后,我们就可以在组件的 props 中访问到对应的仓库数据了:

function Counter(props)
    render() {
        return (
            

计数器

{props.数据名}

) } }
7.2、修改仓库数据

修改仓库数据,依然是通过 dispatch() 方法来触发修改操作。

在组件中,只要和仓库关联过,就能在 props 上直接获取到 dispatch 方法。因此,在组件中可以直接通过 props.dispatch() 方法来触发修改数据的操作。

import React,{useEffect,useState} from 'react'
import store from '../redux/store'
import {incrementAction} from '../redux/actions'
import { connect } from 'react-redux';

function Counter(props) {
  const increment=()=>{
      props.dispatch(incrementAction(3))
  }
  return (
    

计数器:{props.num}

) } const mapStateToProps = (state) => { console.log(state); // 仓库中所有的数据 return{ num:state.num } } export default connect(mapStateToProps)(Counter)

七、状态机的Hook

针对 React 中的函数组件,React-Redux 中也提供了第三方的 Hook。

1、useSelector

通过调用 useSelector 方法,并传递一个回调函数作为参数,我们可以在这个回调函数中获取到仓库中的 state。

import { useSelector } from 'react-redux'
useSelector((state) => {
    console.log(state);  // 仓库中所有的数据
})

然后我们可以在回调函数中,将我们需要使用的数据 return 出来,然后用一个变量来接收:

const data = useSelector((state) => {
    return 数据;
})

后续组件要使用数据,就可以直接通过变量进行数据的访问了。

2、useDispatch

调用 useDispatch() 方法,可以直接获取到 dispatch() 方法。

import { useDispatch } from 'react-redux'
export default fucntion Test() {
    const dispatch = useDispatch();
    return (
    )
}

如果组件中使用 Hook 来获取 dispatch 方法的话,就不再需要使用 connect 来对组件和仓库进行关联了。

获取到 dispatch 方法后,后续的使用就和 props.dispatch 的使用一致。

关键代码

import React from 'react'
import {useDispatch,useSelector} from 'react-redux'
import {incrementAction} from '../../redux/action'


export default function Counter(props) {
  const num=useSelector((state)=>{
    return state.num
  })
  const dispatch=useDispatch()
  const increment=()=>{
    dispatch(incrementAction(3))
  }
  return (
    

计数器:{num}

) }

八、reducer拆分

当项目越来越大的时候,需要管理的数据也会越来越多,如果所有的数据都由一个reducer管理的话,则这个reducer肯定会变得非常的臃肿,且难以维护。所以有必要对reducer做一个拆分,不同功能模块的数据切片,由不同的reducer来管理。假设现在有两个模块,账户管理模块和商品管理模块,每个模块都有数据需要管理

import {combineReducers} from 'redux'
import counterReducer from './counterReducer'
export default combineReducers({
    counter:counterReducer
})

注意:调用时候需要使用使用到切片的名字才能访问到,比如

import React from 'react'
import {useSelector} from 'react-redux'
export default function Counter(props) {
  const count=useSelector((state)=>{
    console.log(state);
    return state.counter.num      //state.切片名的key.num
  })
  return (
    

计数器:{count}

) }

你可能感兴趣的:(react.js,笔记,前端)