当前位置: 首页 > 工具软件 > react-cursor > 使用案例 >

React-Tookit安装以及使用案例

令狐阳秋
2023-12-01

title: React Toolkit
date: 2022-09-29 14:42:35
tags:

  • React
  • Redux
  • 框架
  • TypeScript
    categories:
  • React

React Toolkit

首先,React Tookit是React新提出的类Redux状态管理模式。该技术的提出是为了解决Redux的三个常见问题:

  • 廊”配置Redux储存太复杂了“
  • 樂”必须添加很多包才能让Redux做任何有用的事情“
  • ”Redux需要太多样板代码“

更多介绍可以看官方文档

安装

官方提供了基于 React+Js 或者 React+Ts 的模块(脚手架):

# Redux + Plain JS template
npx create-react-app my-app --template redux

# Redux + TypeScript template
npx create-react-app my-app --template redux-typescript

如果你想在已有的项目上安装,使用如下命令:

# NPM
npm install @reduxjs/toolkit
or
# Yarn
yarn add @reduxjs/toolkit

当然有你需要先有react-redux

使用

官方的例子是一个加减demo,我这里是一个购物车的案例

安装

npm install @reduxjs/toolkit react-redux

创建Redux-Toolkit

一个项目仅有一个state,和redux一样,也是需要将各个分开的状态统一整合管理

注意:counterSliceshopCarList是用户自定义的slices,在redux中称之为reducer

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

import counterSlice from './slices/slices_shoplist'; // 商品列表state
import shopCarList from "./slices/slices_shopCar"; // 购物车state

const store = configureStore({
    reducer:{
        counter:counterSlice, // 配置多个slices
        shopCar:shopCarList // 更多slices
    }
})

export type RooState = ReturnType<typeof store.getState>  // 类型生成

export type AppDispatch = typeof store.dispatch  // 类型生成

export default store

index.ts文件中引入<Provider>组件

import ReactDOM from 'react-dom/client';
import App from './App';

import { Provider } from 'react-redux'; // 引入第三方监视组件,用于传入state
import store from './redux/store'; // 引入公共state

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

// 包裹App组件
root.render(
    <Provider store={store}> 
      <App />
    </Provider>
);

创建各个状态文件在redux/slices/slices_shoplist

ts类型注解不做过多解释,PayloadAction是官方提供的TS接口,<>中注解使用该reducer传入参数的类型

每个reducer方法都有形参state,actionstate指向状态,action中的action.payload代表调用该reducer方法的时候传入的参数

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

export interface initArr {
    key:number,
    title:string,
    price:number,
    quantity:number
}

interface initType {
    shopList:Array<initArr>
} 

// 初始状态
const initial:Array<initArr> = [
    {
        key:1,
        title:'测试文本01',
        price:100,
        quantity:100,
    },
    {
        key:2,
        title:'测试文本02',
        price:200,
        quantity:100,
    },
    {
        key:3,
        title:'测试文本03',
        price:300,
        quantity:100,
    },
];


const initialState:initType = {
    shopList:initial
}

const counterSlice = createSlice({
    name:'counter',
    initialState, // 初始状态
    reducers:{ // reducer
        addShop:(state,action:PayloadAction<initArr>)=>{
            // 添加商品功能
            state.shopList.push(action.payload)
        },
        deleShop:(state,action:PayloadAction<number>)=>{
            // 删除指定商品功能
            state.shopList = state.shopList.filter((tit:initArr)=>{
                return tit.key !== action.payload
            })
        }
    },
})

export const {
    addShop,   // 将各个reducer暴露出去
    deleShop
} = counterSlice.actions;

export default counterSlice.reducer;

在组件中使用,在函数组件中使用钩子useSelector,useDispatch获取初始状态以及dispatch,代码过多请仅关注标注部分

import React, { useRef } from 'react'
import { useSelector,useDispatch} from 'react-redux'  // 引入两个必要的钩子
import type { RooState } from '../../redux/store'; // 引入生成的类型注解

import styled from 'styled-components';
import { addShop } from '../../redux/slices/slices_shoplist'; // 引入使用的reducer

export const OneDiv = styled.div`
  h1{
    font-size:25px;
    font-weight:600;
    color:#5592fa;
  }
`
const Input = styled.input`
  border:1px solid #5592fa;
  border-radius:5px;
  width: ${(props)=>props.width};
  height: 30px;
  margin-top:20px;
  font-size:18px;
  padding-left:10px;
  outline:none;
`
const Span = styled.span`
  color:#5592fa;
  padding: 0px 9px;
`
const Button = styled.button`
  width:100px;
  height: 30px;
  background-color:#5592fa;
  color:#fff;
  padding: 0px 9px;
  border:none;
  cursor: pointer;
  margin:30px 0px 0px 80px;
`

const One = () => {
  const shopTitle = useRef<HTMLInputElement>(null);
  const shopPrice = useRef<HTMLInputElement>(null);
  const shopQuantity = useRef<HTMLInputElement>(null);
    
  // 获取状态(注意RooState类型就是store中生成的类型)
  const ULS = useSelector((state:RooState)=>state.counter); 
  // 生成dispatch
  const  dispatch = useDispatch();

  const addShopList = ()=>{
    if (shopTitle.current?.value.trim()&&shopPrice.current?.value.trim()&&shopQuantity.current?.value.trim()) {      
      let obj = {
        key:+new Date(),
        title:shopTitle.current?.value,
        price:parseInt(shopPrice.current?.value),
        quantity:parseInt(shopQuantity.current?.value)
      }
      dispatch(addShop(obj)) // 调用方法
      shopTitle.current.value = '';
      shopPrice.current.value = '';
      shopQuantity.current.value = '';
    }else{
      alert('请输入内容');
    }
  }

  return (
    <OneDiv>
      <h1>添加商品</h1>
      <ul>
        <li>
          <Span>商品名称:</Span>
          <Input type="text" width="400px" ref={shopTitle} placeholder="名称"></Input>
        </li>
        <li>
          <Span>商品价格:</Span>
          <Input type="text" width="50px" ref={shopPrice} placeholder="价格"></Input>
        </li>
        <li>
          <Span>商品数量:</Span>
          <Input type="text" width="50px" ref={shopQuantity} placeholder="数量"></Input>
        </li>
        <li>
          <Button onClick={()=>addShopList()}>添加商品</Button>
        </li>
      </ul>
    </OneDiv>
  )
}

export default One

关于异步

官方提供的Demo中的异步属于简化写法,并不会对异步进行监听

export const incrementAsync = (amount) => (dispatch) => {
  // 异步方法存放于此
  setTimeout(() => {
    dispatch(incrementByAmount(amount)) // 可在此调用同步中的reducer方法
  }, 1000)
}

第二种写法:

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

export var loadPic = createAsyncThunk('weather/loadPic', async () => {
    return new Promise((resolve:(str:string)=>void,reject)=>{
        setTimeout(()=>{
            resolve('数据');
        },1000)
    }) // 此处的返回结果会在 .fulfilled中作为payload的值
});

var counterSlice = createSlice({
    name:'counter',
    initialState:{
        value:0 // 初始值
    },
    reducers:{ // reducer
        incremented:(state)=>{
            state.value+=1
        },
        decremented:(state)=>{
            state.value-=1
        },
        add:(state,action)=>{
            // action的playload是调用该函数的时候传入的值
            state.value+=action.payload;
        }
    },
    // 第一种写法
    // extraReducers:{
    //     [loadPic.pending.type](state,action:any){
    //         console.log('pending',state,action);
    //     },
    //     [loadPic.fulfilled.type](state,action:any){
    //         console.log('fulfilled',state,action);
    //         state.value+=1;
    //     },
    //     [loadPic.rejected.type](state,action:any){
    //         console.log('rejected',state,action);
    //     },
    // }
    // 第二种写法
    extraReducers(builder){
        builder
        	// 初始状态
            .addCase(loadPic.pending,(state)=>{
                console.log(state);
            })
            // 成功状态
            .addCase(loadPic.fulfilled,(state,action)=>{
                console.log(state);
                console.log(action.payload);//'数据'  异步请求到的数据
            })
    }
})


export const {incremented,decremented,add} = counterSlice.actions;

export default counterSlice.reducer;

异步方法使用:

  ...

  <button onClick={()=>{dispatch(getMovieData())}}>获取数据</button>

  ...
 类似资料: