08-redux状态管理

1. 什么是Redux

redux 是一个独立专门用于做状态管理的 JS 库(不是 react 插件库)

2. 为什么使用Redux

  • 因为对于react来说,同级组件之间的通信尤为麻烦,或者是非常麻烦了,所以我们把所有需要多个组件使用的state拿出来,整合到顶部容器,进行分发。

  • 首先明确一点,Redux 是一个有用的架构,但不是非用不可。事实上,大多数情况,你可以不用它,只用 React 就够了

3. Redux的应用场景

从组件角度看,如果你的应用有以下场景,可以考虑使用 Redux。

  • 某个组件的状态,需要共享
  • 某个状态需要在任何地方都可以拿到
  • 一个组件需要改变全局状态
  • 一个组件需要改变另一个组件的状态

4. 面试题:Redux的三个核心概念

4.1 store

Redux的核心是store,它由Redux提供的 createStore(reducer) 这个方法生成

三个相关API:

store.getState():在组件中获取存储的store数据;

store.dispatch(action):分发action,并返回一个action,这是唯一能改变store中数据的方式;

subscribe(listener):注册(订阅)一个监听者,store发生变化的时候被调用。

4.2 reducer

reducer是一个纯函数,它根据previousState和action计算出新的state。指定了应用状态的变化如何响应action并发送到store的。
reducer(previousState,action)

4.3 action

action本质上是一个JavaScript对象,其中必须包含一个type字段来表示将要执行的动作,其他的字段都可以根据需求来自定义。数据的唯一来源,描述了有事情发生这一事实。

const ADD_TODO = 'ADD_TODO'
{
  type: ADD_TODO,
  value: '数据'
}

5. 面试题:Redux的工作流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eKex1aKM-1678248990019)(ipic\10022375-ca04027f2fedac55.webp)]

  1. 用户在UI组件中通过 store.dispatch触发action ;
  2. store 自动调用reducer,并传入之前的state,以及用户的action, 经过reducer返回新的state;
  3. 组件通过store.subscribe(listener) 订阅state的变化,可通过setState更新react UI。

6.案例:class类组件中Redux的使用步骤

  • 需求分析
    1. 创建可以被所有组件共享的store数据city和color
    2. 在组件A中获取store中的city和color
    3. 在组件B中改变store中的city, 观察组件A中city和store中city同步

6.1 安装redux

npm i redux@4.1.2 -S

6.2 创建store

  • 在src文件夹下创建store文件夹,在store下创建index.js

  • index.js代码

    //1. 引入redux模块的createStore方法
    import { createStore } from 'redux'
    
    //2. 配置store中state的初始值
    let defaultValue = {
        city: '郑州',
        color: 'red'
    }
    
    //3. 用createStore创建store
    //createStore的参数必须是一个回调函数,该回调称为reducer
    const store = createStore((state = defaultValue, action) => {
        
        //4. 根据action的type类型对state进行处理,并返回最新的state
        
        //更改城市
        if (action.type === "CHANGE_CITY"){
            //!!!!!!不能直接更改state,直接更改,组件不会更新
            let newState = JSON.parse(JSON.stringify(state))
            //action.value为传来的新值
            newState.city = action.value
            return newState
        }
        // 更改颜色 
        if (action.type === "CHANGE_COLOR"){
            let newState = JSON.parse(JSON.stringify(state))
            newState.color = action.value
            return newState
        }
        return state
    })
    
    export default store
    
 > reducer处理store的改变,第一个参数为传入的当前的state值,action为要执行的动作
 > reducer是一个纯函数,它根据previousState和action计算出新的state。
 >
 > action是组件在改变state时传过来的数据,是一个对象,其中必须包含一个type字段来表示将要执行的动作

6.3 在class组件A中读取store数据

ChildA.js

import React, { Component } from 'react'
//1. 引入store
import store from './store'

export default class ChildA extends Component {
    state = {
        //2. 获取store数据
        ...store.getState()
    }
    componentDidMount(){
        //3. 在组件创建时,订阅当store中数据改变时要执行的函数storeChange,
        store.subscribe(this.storeChange)
    }
	//4. store中数据改变时要执行的函数
    storeChange = ()=>{
        this.setState(store.getState())
    }
    render() {
        return (
            <div>
                <h1>ChildA</h1>
                {/* 5. 使用store中的数据 */}
                <p>城市:{this.state.city}</p>
                <p>颜色:{this.state.color}</p>
            </div>
        )
    }
}

6.4 在class组件B中更改store数据

ChildB.js

import React, { Component } from 'react'
// 1. 引入store
import store from './store'

export default class ChildB extends Component {
    
    //2. 定义更改store数据的方法
    changeCity = () => {
        //3. 定义action
        let action = {
            type: "CHANGE_CITY",
            value: '纽约'
        }
        //4. 用dispatch向store发送action
        store.dispatch(action)
    }
    render() {
        return (
            <div>
            	{/* 5. 绑定事件,改变数据 */}
                <button onClick={this.changeCity}>改变城市</button>
            </div>
        )
    }
}

7. 结合react-redux在函数组件中使用store

7.1 安装 react-redux

npm i react-redux@8.0.5

7.2 在应用中注入store

App.js

import React from 'react'
import "./App.css"
//引入路由中的各种API
import {
  BrowserRouter as Router,
} from 'react-router-dom'

import RenderRouter  from './routes'
import TabBar from './TabBar'
//****************引入store
import store from './store'

//****************引入Provider组件
import {Provider} from 'react-redux'

export default function App() {
  return (
    <Provider store={store}>
      <Router>
         <RenderRouter></RenderRouter>
         <TabBar></TabBar>
      </Router>
    </Provider>
  )
}

7.3 在函数组件中使用store

import store from './store'
// useSelector Hook 用来获取store数据
// useDispatch Hook 用来引入dispatch方法,触发action,更改store
import { useSelector,useDispatch } from 'react-redux'

export default function Cart() {
  const {city} = useSelector(state=>state)
  const dispatch = useDispatch()

  const change = ()=>{
    let action = {
      type: 'CHANGE_CITY',
      value: '北京'
    }
    dispatch(action)
  }
  return (
    <div>
      city: {city}
      <hr />
      <button onClick={change}>改变city</button>
    </div>
  )
}


8. 扩展: 拆分reducer和actionType

8.1 为什么拆分reducer

如果一个项目,比较大,需要redux存储的状态数据比较多时,reducer.js无疑是会非常臃肿的。所以为了简化reducer.js文件,我们应该按照功能模块将这个大的reducer.js文件,拆分成若干个reducer.js。那么这里就需要使用redux里面的一个方法:combineReducers

8.2 为什么拆分actionType

首先当action.type不拆分的话在组件中的actionType要对应到reducer里的actionType,并且一模一样

当你操作时type如果差一个字符是不会执行Reducer的action的 且控制台不会报错。所以要把actionType拆分出来 当做变量引入时,当你输入错误,控制台会报错。这时查找原因会非常容易。

8.3 步骤

  • 在store中创建actionType.js

    export const CHANGE_CITY = 'changeCity'
    export const CHANGE_COLOR = 'changeColor'
    
  • 在store中创建cityReducer.js

    可单独管理与城市相关的数据和操作

    import {CHANGE_CITY} from './actionType'
    
    const defaultValue = {
      city: '北京',
    }
    const cityReducer = (state=defaultValue,action)=>{
      let {type,value} = action
      if (type === CHANGE_CITY){
        const newState = JSON.parse(JSON.stringify(state))
        newState.city = value
        return newState
      }
      return state
    }
    export default cityReducer
    
  • 在store中创建colorReducer.js

    import {CHANGE_COLOR} from './actionType'
    const defaultValue = {
      color: 'red'
    }
    const colorReducer =  (state=defaultValue,action)=>{
      let {type,value} = action
      if (type === CHANGE_COLOR){
        const newState = JSON.parse(JSON.stringify(state))
        newState.color = value
        return newState
      }
      return state
    }
    export default colorReducer
    
  • 修改store/index.js中的代码

    import { createStore,combineReducers } from 'redux'
    import cityReducer from './cityReducer'
    import colorReducer from './colorReducer'
    
    //使用combineReducers方法合并reducer
    const reducer = combineReducers({
        a: cityReducer,
        b: colorReducer
      })
      
      //createStore的回调函数称为reducer
      const store = createStore(reducer)
      export default store
    

如果访问cityReducer中数据,在组件中需要指定模块名 this.state.a.city, 其它使用方法都不变

9. actionCreator拆分action

作用: actionCreator: 统一管理action

store/actionCreator.js

import {CHANGE_CITY} from './actionType'
export const getCity = (value)=> (
    {
        type: CHANGE_CITY,
        value
    }
)

ChildB.js

	const dispatch = useDispatch()
    
    const changeCity = () => {
        const action = getCity('北京')
        dispatch(action)
    }

10. redux-thunk中间件

redux的中间件,专门用来处理异步请求,使dispatch可以接受一个函数做为参数,从而从组件中把异步请求封装到actionCreator中

  • 安装

    npm i redux-thunk@2.4.1 -S
    
  • store/index.js

    import { createStore,combineReducers,applyMiddleware } from 'redux'
    import thunk from "redux-thunk";
    import cityReducer from './cityReducer'
    import colorReducer from './colorReducer'
    
    const reducer = combineReducers({
        a: cityReducer,
        b: colorReducer
    })
    const store = createStore(reducer,applyMiddleware(thunk))
    export default store
    
  • 不使用redux-thunk的异步请求

    dispatch的action只能是一个对象,异步操作必须放在组件中

  • 使用redux-thunk的异步请求

    dispatch的action可以是一个有异步操作的函数,从而在actionCreator中集中管理异步请求

    • actionCreator.js

      export const getList = (dispatch)=>{
          setTimeout(() => {
              let list = ['上海', '开封', '安阳']
              let action = {
                  type: CHANGE_CITYLIST,
                  value: list
              }
              dispatch(action)
          }, 2000)
      }
      
    • ChildA.js

     import { getList } from './store/actionCreator'
     //省略其它代码
     const dispatch = useDispatch()
         useEffect(() => {
             dispatch(getList)
         }, [dispatch])
    
     let list = ['上海', '开封', '安阳']
                 let action = {
                     type: CHANGE_CITYLIST,
                     value: list
                 }
                 dispatch(action)
             }, 2000)
         }
    
    • ChildB.js

      import { getList } from './store/actionCreator'
      //省略其它代码
      const dispatch = useDispatch()
          useEffect(() => {
              dispatch(getList)
          }, [dispatch])
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值