React-redux 状态管理

283 阅读8分钟

什么时候用呢?

就像眼镜一样,你该用到的时候你就会知道

  • 小型项目当中没有涉及特别复杂的数据传递,但是到了一些大型项目,不同组件之间的传递的数据越来越多,可能使用redux就能减少这些状态管理的负担

redux

  • 是react生态当中的状态管理

官网地址

redux.js.org/

两种模式

  • react-redux 传统方式
  • RTK 便捷,开箱即用

react-redux 传统方式

  • 依赖安装
npm i react-redux redux 

创建一个store对象

store/index.js

import { createStore } from 'redux';
// 1. 创建store
//  createStore(reduce函数(上一次返回的值,触发的时候传递的参数))

// initState设置一个初始值
const initState = {
    count: 0
}
 const store = createStore((state=initState, action)=>{

    if(action.type === 'increCt'){
       state.count = state.count + action.payload
     }
     
    // 通过返回值来更新store中的值
    return {...state }
 });

 export default store

store对象的供应

使用 react-redux 当中的 Provider组件来进行

index.js

import store from './store'; // 引入一个 redux store对象 传统方式
import { Provider } from 'react-redux'; // 引入一个Provider组件 

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
    <Provider store={store}>
        <RouterProvider router={router} />  
    </Provider> 
)

获取组件当中使用 redux全局共享的状态

函数组件 通过useSelector 获取redux共享的状态

import { useSelector } from 'react-redux';

    // redux的使用 useSelector 来获取redux全局共享的状态
   const store = useSelector( state=>{
        //  state就是redux中的共享的数据
        //返回一个新的state 我们实现的对状态的修改,又不想去改变整体,拓展运算符是最优选
        return {...state}
   })

类组件 通过connect 高阶组件 获取redux共享的状态

//让类组件可以通过this.props来访问redux共享的全局状态

import { connect } from "react-redux";

class Uc extends  Component {

    componentDidMount(){
        console.log('uc props',this.props)
    }

    increCtInRedux = ()=>{
   
        // 使用redux的dispatch方法来派发action()
        // 使用 dispatch来让 createStroe中的reduce函数执行 并传递给到 reduce函数的第二个参数
        // 约定传递的这个参数 我们称为 Actions 对象
        // Actions 对象中必须有一个 type 属性
        //createor(20)可以提取出去
        this.props.dispatch(createor(20))
    }
    render() { 
        return (
            <>
                  <div>我是用户中心</div>
                  <div> props in the uc :  { JSON.stringify(this.props) }</div>
                  <button onClick={ this.increCtInRedux }> 测试 dispatch </button>
            </>
          
        )
    }
}

const mapStateToProps = (state)=>{
    // state 就是redux中的共享的数据

    // 返回的是会传递给 类组件的this.props
    return { ...state }
}

export default connect( mapStateToProps )(Uc)

修改状态-就是为了获取dispatch派发函数

类组件 connect

  • 类组件通过 connect之后就可以通过 this.props来使用 dispatch。 通过dispatch来分发一个action对象,dispatch之后会让 store里面的reducer函数执行,然后根据传递的action来对状态进行修改

函数组件 useDispatch

  • 函数组件通过引入 useDispatch 这个hook来获得dispatch方法。 然后通过dispatch分发对象 来修改全局共享状态。
import { useDispatch } from 'react-redux';
 const dispatch = useDispatch();
    const increCtInFC =()=>{
        // dispatch({type:'increCt',payload:1})
        dispatch(createor(5))
   }

两种组件状态的修改

  • 就是通过dispatch()进行派发,派发的是一个acticon 对象,这个action对象必须包含一个属性({type:'xxx'})

关于Action Creator

  • dispatch分发的是 action对象,但是实际工作当中我们一般不会直接书写一个 acion对象而是通过通过一个函数来返回一个 acion对象。这类的函数我们称为 acion creator
export const createor = (payload)=>{
    return {type:'increCt', payload}
}

关于 combineReducer

  • 实际开发过程当中,我们并不会直接在createStore当中单独的使用一个函数来处理所有的业务流程,这样会造成代码的冗余,现在更流行的模块化的开发方式,所以 redux 提供了一个 combineReducer 允许我们把多个reducer函数进行合并。

store/reducers/order.js

  • 这是订单模块
const orderInitState = {
    carts:[],
    total:1000
    }
const OrderReducer = (state = orderInitState, action) => {

    console.log('OrderReducer 执行',action)
    return {...state}
};

export default OrderReducer;

store/reducers/user.js

  • 这是用户模块
const initState = {
    name:'dixon',
    money:1
}

const userReducer = (state=initState,action)=>{
    console.log('userReducer 执行',action)
    return {...state}
}

export default userReducer

模块的合并

store/index.js

import { createStore ,combineReducers} from 'redux';
// 导入2个 reducer 函数
import  userReducer  from './reducers/user';
import orderReducer from './reducers/order';


// combineReducer 用来合并多个reducer
// combine 合并   
const rootReducer = combineReducers({
    user:userReducer,
    order:orderReducer
})

const store = createStore(rootReducer)

 export default store
  • 使用了combineReducer dispatch分发action 会让所有的合并的reducer都会触发,并且会得到同样的action。所以需要按照你的需求去合适恰当的修改 state 但是我们可以通过人为的规则约定 我们在 dispatch "模块名/方法名"的方式来 分发action,以便在开发过程当中提升代码的可读性。
  • 函数虽然都会被执行,但是里面也没有复杂的计算,性能方面不算太差
   const changeOrderTotal = ()=>{

    // 一般来说 如果有多个模块的redux 我们触发的type
    // 会写成下面的样式  "模块名/方法名"
        dispatch({type:'order/setTotal',payload:9999})
   }
   
const orderInitState = {
    carts:[],
    total:1000
}
const OrderReducer = (state = orderInitState, action) => {

    console.log('OrderReducer 执行',action)

    const { type, payload } = action;
    //根据派发的类型进行相应的修改
    if(type==='order/setTotal'){
        state.total = payload
    }
    
    return {...state}
};

export default OrderReducer;

RTK 模式

  • 依赖安装
npm install @reduxjs/toolkit

使用configureStore 创建store对象

store/index.js

import { configureStore } from "@reduxjs/toolkit";

import userReducer from "./slices/user";
import orderReducer from "./slices/order";

const store = configureStore({
    reducer: {
        user: userReducer,
        order: orderReducer
    }
})


export default store

关于slice

  • slice对象,RTK当中提供了 createSlice 方法让我们快捷的创建reducer函数并且附带的创建了触发这些函数对应的 action creator
import { createSlice } from "@reduxjs/toolkit";


const userSlice = createSlice({
  name: "user", // 模块名称 命名空间
  initialState: {  // 初始状态
    name: 'dixon',
    age: 19
  },
  reducers: { // 修改状态的方法
    setName(state,action){
      // state当前的模块的下state
      state.name = action.payload
    },
    setAge(state,action){
      console.log('修改年龄')
      state.age =    state.age - action.payload
    }
  }
})

// createSlice 返回一个slice实例
// slice实例中有两个属性 
// 第一个 actons 是 action creator 用来触发修改状态的方法
//  console.log('userSlice creator',userSlice.actions)
//  console.log('userSlice.actions',userSlice.actions.setName(9999))
export const { setName, setAge } = userSlice.actions

// 第二个 reducer 是一个reducer函数
export default userSlice.reducer

store对象的供应

  • 使用 react-redux 当中的 Provider组件来进行
import store from './store/rtk'; // 引入一个 redux store对象 传统方式
import { Provider } from 'react-redux'; // 引入一个Provider组件


const root = ReactDOM.createRoot(document.getElementById('root'));

root.render( 

<Provider store={store}> <RouterProvider router={router} /> </Provider>

)

获取共享状态

  • 组件当中获取redux 共享状态的方法跟传统方式一致

类组件 connect

  • 类组件通过 connect之后就可以通过 this.props来使用 dispatch。 通过dispatch来分发一个action对象,dispatch之后会让 store里面的reducer函数执行,然后根据传递的action来对状态进行修改

函数组件 useDispatch

  • 函数组件通过引入 useDispatch 这个hook来获得dispatch方法。 然后通过dispatch分发对象 来修改全局共享状态。
import { useDispatch } from 'react-redux';
 const dispatch = useDispatch();
    const increCtInFC =()=>{
        // dispatch({type:'increCt',payload:1})
        dispatch(createor(5))
   }

修改状态

  • 也是使用 dispatch来分发action来实现触发reducer函数来修改状态,但是因为使用了createSlice所以slice对象会给我们提供了对应的 action Creator对应的 actionCretor进行使用即可

slices/users.js

export const { setName, setAge } = userSlice.actions

cp.js

import { setName,setAge } from '../store/slices/user';
import { useDispatch } from 'react-redux';

const dispatch = useDispatch();

const Home = () => {
 const beYoung = ()=>{
        dispatch(setAge(1))
    }
    
  return (
        <div>
            <button onClick={beYoung}> 梦想成真 </button> 
        </div>
    )
  }

关于异步操作

类组件实现方法

使用connect 传入 mapDIspatchToProps 来实现

  • 对于类组件而言我们是可以使用 connect高阶组建成 传入 第二个参数 来实现在组件外部构建一些异步操作的函数,通过connect挂载到 类组件的 this.props
  • 注意事项:如果在connect传入了 第二个参数 mapDIspatchToProps ,dispatch将不再挂载在 this.props上(也就是组件内部得不到distatch)
  • 这种方法可以使得我们抽离逻辑,实现模块化的开发

image.png

函数组件实现方法

使用中间件 redux-thunk

  • 安装 redux-thunk
npm i redux-thunk
  • 配置 在store/index.js image.png

  • 配置后之之后 我们的dispatch 不仅仅能传入一个action对象也可以thunk中间件的主要用途就是允许我们在 dispatch的传入一个函数而不一定要是一个action对象,而这个传入的函数将会得到一个dispatch的参数 方便其在内部完成了异步操作之后 分发action对象

image.png

  • 这种方法可以使得我们抽离逻辑,实现模块化的开发

数据持久化

  • redux-persist 可以帮我们快捷的实现redux共享数据的持久化,避免刷新页面之后导致共享的数据重置的麻烦。
  • 安装 redux-persist
npm i redux-persist
  • 引入对应的持久化类库
// 引入对应的持久化的类库
import { persistStore,persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // localStorage
import storageSession from 'redux-persist/lib/storage/session'; // sessionStorage
  • 配置存储配置的信息模板

const persistConfig = {
    key:'root',// 在浏览器的本地存储当中 使用哪个key来保存redux 的全局状态的数据
    storage: storage , // 使用localStorage还是sessionStorage来保存数据 上面的storage和storageSession二选1
}
  • 多个module 使用combineReducers()时
  • 使用persistReducer(配置信息, reducer)单独的给每个reducer函数进行持久化处理
  • 在原有的reduer函数当中使用 persistReducer来进行处理
const rootReducer = combineReducers({
    user: persistReducer({ key:'user' ,storage} ,userReducer), 
    // 使用persistReducer(配置信息, reducer)单独的给每个reducer函数进行持久化处理
    order: persistReducer({key:'order', storage} ,orderReducer)
})

const store = createStore(rootReducer,applyMiddleware(thunk))
  • 单独的导出一个持久化对象
export const persistor = persistStore(store) // 还要单独的生成一个持久化对象

 export default store
  • 使用持久化对象
import { PersistGate } from 'redux-persist/integration/react'
import store ,{persistor} from './store' 

const App = () => {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        原来的代码
      </PersistGate>
    </Provider>
  );
};