redux的@reduxjs/toolkit + Hooks用法

新特性的出现都是解决了旧用法的一些不足之处 !

1. 下载 @reduxjs/toolkit    npm install @reduxjs/toolkit

再下载 react-redux        npm install react-redux  // 这里还要用到react-redux, 是因为连接组件和数据的时候, 仍然使用的是react-redux, @reduxjs/toolkit 只是替代了redux的方案

 2. 在index.js中引入使用

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import store from './hookRedux/index' // hook组件的redux
import { Provider } from "react-redux";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Provider>
);

我的目录结构如下

hookPage里面都是容器组件(所谓容器组件就是, 使用react-redux连接了数据和页面的组件)

hookRedux里面都是store相关的数据, 包括全局属性, 全局状态, 全局事件等

 在App.js中引入组件

// App.js 的代码
import React from 'react'
import Name from './hookPage/name'
import Counter from './hookPage/counter'
function App() {
  return (
    <div>
      <Name />
      <Counter />
    </div>
  )
}
export default App

3. 从 @reduxjs/toolkit开始实现全局状态

在@reduxjs/toolkit(下面简称rt)中, 导出了一个 配置函数,configureStore , 该函数返回的结果指向了一个合并了所有reducer的容器, 在这里可以做一个统一的出口, 代码如下

// hookReducer 的index.js的文件
import { configureStore } from "@reduxjs/toolkit";
// counterReducer状态管理store , 下一步会写到
import counterReducer from "./modules/counter";
// nameReducer状态管理的store
import nameReducer from './modules/name'
const store = configureStore({
    reducer: {
        counterReducer,
        nameReducer
    }
})

// 最终导出的store, 也就是index中的store, 
// 是包含了counterReducer 和 nameReducer在内的store
export default store

4. 各个状态store的配置, 这里就使用一个来举例子, 写法都一致, 以nameReducer来举例子

在rt中导出了一个createSlice, 从定义的名字上来看, slice意为 “片段、部分”, 就是创建一个局部的store, 最终将多个局部的片段来合并起来, 就是步骤3里面提到的。

name: 字符串,当前片段的名字, 需要定义一个name来区分各个片段;

initialState:对象, 初始定义的值, 也就是默认值, 当没有其他任务来改变的时候, 就会默认使用默认的值

reducer:对象, 定义修改initialState的值的方法, 修改initialState的值, 只能通过reducers内的方法来修改, (与vue的actions相似) 

代码如下

// hookRedux下的module下的name.js代码
import { createSlice} from "@reduxjs/toolkit";

const nameSlice = createSlice({
  name: "counter",
  initialState: {
    name: '天',
  },
  reducers: {
    changeName(state, { payload }) {
      state.name = payload;
    },
  },
});

// 最终默认导出的就是当前片段的store
export default nameSlice.reducer;
// 定义导出的都是reducers里的方法
export const { changeName } = nameSlice.actions;

// 注意!!! 这里的reducer和actions都是定义的, 不能自己修改

5.页面组件使用store, 首先确定一点, 这里说的store并不是片段的store、而是全局的store, 因为全局的store是要给所有的组件提供使用的; 其次, 当前的页面组件要用到全局store的某个片段store, 需要返回来使用

如下代码所示, 我要在当前的页面组件中获取到全局store的name片段store里的name, 就是步骤4里面定义的name, 并且对这个name进行修改, 将他修改为当前的时间。

5.1 先导入要修改的方法, 因为步骤4说了, 要修改initialState里的值, 只能通过方法来修改, 所以要引入changeName方法

5.2 react-redux提供了 {useSelector, useDispatch, shallowEqual} hook函数, 

        5.2.1 useSelector, 这是一个获取到全局store的hook函数, 在这里可以拿到当前页面组件所需要的store数据, 如下代码, useSelector的第一个回调函数的state参数, 该参数就是全局store里的所有数据, 也就是hookRedux下的index.js的默认导出的store里的reducer, 如下图所示

 然后再通过解构获取到name, 这个name就可以在当前的页面组件中展示了;

        5.2.2 shallowEqual useSelector的第二个参数, 意为浅拷贝, 这个是一个优化处理的参数, 有了这个参数, 如果其他的数据修改, 并不会重新渲染当前页面组件, 举例子, 如果counterReducer里的某个数据变化了, 在这个页面组件中, 因为没有用到counter, 所以这里不会重新渲染, 这是一个优化的参数。

        5.2.3 useDispatch 之前多次提到, 要修改initialState里的数据, 要使用方法来修改, 这个方法已经被我们引入了, changeName方法。但这里不是直接调用, 而是要使用dispatch来派发调用, 该hook返回一个函数, 将要派发的方法传入即可。 如下代码所示。

当点击changeName的时候, 就会执行cn函数, cn派发了changeName函数, 修改了initialState里name的值, 当name变化了, 当前组件重新渲染, 对应的name也发生变化, 页面数据也就会改变

// hookPage下的 name.jsx 的代码
import React from "react";
import { changeName } from "../hookRedux/modules/name";
import {useSelector, useDispatch, shallowEqual} from 'react-redux'

export default function Name() {
  console.log('name')
  const {name} = useSelector(state => {
    return {
      name: state.nameReducer.name,
    }
  }, shallowEqual)
  const dispatch = useDispatch()

  function cn(value) {
    dispatch(changeName(value))
  }
  return (
    <div>
      <p>{name}</p>
      <p onClick={() => cn(new Date().toTimeString())}>changeName</p>
    </div>
  )
}

6. rt的异步操作

异步操作是放在 createAsyncThunk中来实现的, 同样的, 他也是一个rt中导出的函数,第一个字符串参数, 在你的调试器里显示标明当前的任务如图所示。

 第二个参数, 是一个回调函数, 一般在这里发起网络请求, 这个fetchHomeMultidataAction函数接受到的参数, 就会被放到value里去。 这里return 的数据将会在extraReducers中去

在extraReducers中对某个异步任务会触发3中不同状态的处理, pending 、fulfilled、 reject状态,一般只需要处理fulfilled、 将得到的数据放到state中去。

// hookRedux 下的module 下的name.js 的代码
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from 'axios'

// 外部组件调用这个异步函数即可
// 第二个回调函数参数, 是一个请求函数,一般在开发中是会import引入来使用的
export const fetchHomeMultidataAction = 
// 这里的第一个字符串参数, 是在调试器里显示的当前在执行的任务的名字
createAsyncThunk('fetch/homemultidata', async (value) => {
  const res = await axios.get('http://123.207.198xxxxxxxxx')
  return res.data
})
const nameSlice = createSlice({
  name: "counter",
  initialState: {
    name: '天',
    banners: [],
    recommends: [],
  },
  reducers: {
    changeName(state, { payload }) {
      state.name = payload;
    },
  },
    // 异步的操作
    extraReducers: {
      // [fetchHomeMultidataAction.pending](state, action) {
      //   console.log('pedding')
      // },
      [fetchHomeMultidataAction.fulfilled](state, {payload}) {
        state.banners = payload.data.banner.list
        state.recommends = payload.data.recommend.list
      },
      // [fetchHomeMultidataAction.rejected](state, action) {
      //   console.log('reject')
      // }
    }
});

export default nameSlice.reducer;
export const { changeName ,changeBanners,changeRecommends} = nameSlice.actions;

7. 页面组件中调用, 发起请求得到数据放到页面上。 从useSelector中结构出来的bannerList, 当派发了请求函数并执行之后, banners发生了变化, 当前页面组件重新执行, store数据渲染在页面上。

// hookPage下的name.jsx代码
import React from "react";
import { changeName,fetchHomeMultidataAction } from "../hookRedux/modules/name";
import {useSelector, useDispatch, shallowEqual} from 'react-redux'

export default function Name() {
  console.log('name')
  const {name, bannerList} = useSelector(state => {
    console.log(state, 'state')
    return {
      name: state.nameReducer.name,
      bannerList: state.nameReducer.banners
    }
  }, shallowEqual)
  const dispatch = useDispatch()

  function cn(value) {
    dispatch(changeName(value))
  }
// 在cb函数中, dispatch去派发请求的任务, 这样就能调用请求
  function cb(value) {
// 当这一步执行完毕之后, store里的数据已经被修改了
    dispatch(fetchHomeMultidataAction(value))
  }

  return (
    <div>
      <p>{name}</p>
      <p onClick={() => {cb('333')}}>changeBanners</p>
      <p onClick={() => cn(new Date().toTimeString())}>changeName</p>
      {
        bannerList.map(item => {
          return (<p key={item.acm}>{item.title}</p>)
        })
      }
    </div>
  )
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值