学习React之redux-toolkit

118 阅读2分钟

学习React之redux-toolkit

通过构建一个小案例进行学习。

需求:1、开发一个计数器。2、异步获取数据并展示

实现计数器:

步骤一:使用@reduxjs/toolkit库中的createSlice() API初始化state数据并定义加法和减法的reducer,暴露这个切片对象的reducer和actions

新建一个feature文件夹专门管理组件的状态,在文件夹下新建counter.js

import { createSlice } from "@reduxjs/toolkit"

const counterSlice = createSlice({
    // 为切片命名
    name: "counter",
    // 初始化state
    initialState: {
        counter: 0,
    },
    // 注意是reducers,同时是一个对象
    reducers: {
        // 在这里函数名addNumber就对应上一节中redux中的action
        // 用户派发action时可以传参数,用户传来的参数就是这里的payload,运用了解构操作
        // 用户派发action时redux-toolkit会默认给action传入当前的state,便于用户使用state
        addNumber(state, {payload}) {
            // 可以直接操作state而不用像redux一样重新返回一个对象
            // 原因:使用了immerjs,采用了持久化数据结构和结构共享,保证每一个对象都是不可变的,任何添加、修改、删除等操作都会生成一个新的对象,且通过结构共享等方式大幅提高性能
            state.counter += payload
        },
        subNumber(state, {payload}) {
            state.counter -= payload
        }
    }
})
// 暴露actions
export const { addNumber, subNumber } = counterSlice.actions
// 暴露reducer
export default counterSlice.reducer

步骤二:利用configureStore() API创建store对象,并将counterSlice的reduce添加到store中

import {configureStore} from '@reduxjs/toolkit';
import counterReducer from './features/counter'
import bannerReducer from './features/banner'

const store = configureStore({
    reducer: {
        counter: counterReducer,
        banner: bannerReducer
    }
})

export default store;

步骤三:书写组件逻辑

import React, { PureComponent } from 'react'
import { connect } from "react-redux"
import { addNumber, subNumber} from '../store/features/counter'

// 实现计数器
export class Home extends PureComponent {
  render() {
    return (
      <div>
        <h2>Home</h2>
        <h3>当前计数:{this.props.counter}</h3>
        <button onClick={e => this.props.addNumberWithHome(1)}>+1</button>
        <button onClick={e => this.props.subNumberWithHome(1)}>-1</button>
      </div>
    )
  }
}

const mapStateToProps = (state) => {
    return {
        // 这里要注意
        counter: state.counter.counter
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        addNumberWithHome(num) {
            dispatch(addNumber(num))
        },
        subNumberWithHome(num) {
            dispatch(subNumber(num))
        }
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Home)

实现异步请求:

import {createSlice, createAsyncThunk} from "@reduxjs/toolkit"
import axios from "axios"

export const fetchBannerDataAction = createAsyncThunk(
    "fetch/homeBannerData",
    async (extraInfo, {dispatch, getState}) => {
        console.log(extraInfo, dispatch, getState)
        const res = await axios.get("http://123.207.32.32:8000/home/multidata")
        const banners = res.data.data.banner.list
        // 不写extraReducers的方法的写法
        dispatch(changeBanners(banners))
        
		// 写extraReducers的方法的写法得到res之后直接return res.data即可
        // 但是在slice中要写对应的extraReducer
        return res.data
    })

const bannerSlice = createSlice({
    name: "banner",
    initialState : {
        banners: []
    },
    reducers: {
        changeBanners(state, {payload}) {
            // 原因:使用了immerjs,Immutable.js 采用了持久化数据结构和结构共享,保证每一个对象都是不可变的,任何添加、修改、删除等操作都会生成一个新的对象,且通过结构共享等方式大幅提高性能
            state.banners = payload
        }
    },
    // extraReducers写法一
    extraReducers: {
        [fetchBannerDataAction.pending](state, action) {
            console.log("fetchBannerDataAction pending")
        },
        [fetchBannerDataAction.fulfilled](state, {payload}) {
            console.log("fetchBannerDataAction fulfilled",payload)
            state.banners = payload.data.banner.list
        },
        [fetchBannerDataAction.rejected](state, action) {
            console.log("fetchBannerDataAction rejected")
        },
    }
    // extraReducers写法二
    // extraReducers: (builder) => {
    //     builder
    //         .addCase(fetchBannerDataAction.pending, (state, action) => {
    //             console.log("fetchBannerDataAction pending")
    //         })
    //         .addCase(fetchBannerDataAction.fulfilled, (state, {payload}) => {
    //             console.log("fetchBannerDataAction fulfilled", payload)
    //             state.banners = payload.data.banner.list
    //         })
    //         .addCase(fetchBannerDataAction.rejected, (state, {payload}) => {
    //             console.log("fetchBannerDataAction rejected")
    //         })

    // }
})

export const {changeBanners} = bannerSlice.actions

export default bannerSlice.reducer

组件代码:

import React, { PureComponent } from 'react'
import {connect} from "react-redux"
import {fetchBannerDataAction} from '../store/features/banner'

export class Profile extends PureComponent {
  // 在这里进行异步获取数据请求
  componentDidMount() {
    this.props.fetchBannerData()
  }

  render() {
    return (
      <div>
        <h2>Profile</h2>
        <ul>
          {
            this.props.banners.map((item, index) => {
              return <li key={index}>{item.title}</li>
            })
          }
        </ul>
      </div>
    )
  }
}

const mapStateToProps = state => ({
  banners: state.banner.banners
})

const mapDispatchToProps = dispatch => ({
  fetchBannerData() {
    dispatch(fetchBannerDataAction({name: "张华栋"}))
  }
})

export default connect(mapStateToProps, mapDispatchToProps)(Profile)

以后直接看官方文档即可