React18中immer和Redux Toolkit的使用


1. immer

概述:

它和immutable相似的,实现了操作对象的数据共享,可以优化性能。它实现的原理使用es6的Proxy完成的。小巧,没有像immutable哪样有新数据类型,而是使用js类型。

安装:

yarn add immer@9

1.1 setState结合immer使用

简单使用:

import React from 'react'
// 把源数据使用Proxy进行代理处理,后面就可以精准的去找到变化的数据
import {
    
     produce } from 'immer'

const state = {
    
    
  count: 1
}
// 进行代理后,并修改
// draft就是代理对象,它就是state代理对象
const newState = produce(state, draft => {
    
    
  draft.count++
})

console.log(newState.count)// 2

const App = () => {
    
    
  return <div></div>
}

export default App

使用immer进行proxy代理,源数据中只有变化的了的数据才更新,没有变化则共享,这样做可以提高性能:

import React from 'react'
// 把源数据使用Proxy进行代理处理,后面就可以精准的去找到变化的数据
import {
    
     produce } from 'immer'

const baseState = {
    
    
  arr: [1, 2, 3],
  obj: {
    
     id: 1, name: '张三' }
}

// 使用immer进行proxy代理,源数据中只有变化的了的数据才更新,没有变化则共享,提高性能
const newState = produce(baseState, draft => {
    
    
  draft.arr.push(4)
})

// 当前只修改数组,对象没有修改,共享
console.log('arr', newState.arr === baseState.arr) // false
console.log('obj', newState.obj === baseState.obj) // true

const App = () => {
    
    
  return <div></div>
}

export default App

在这里插入图片描述

使用 immer 优化 setState 使用:

import React, {
    
     Component } from 'react'
import {
    
     produce } from 'immer'
// 优化setState更新数据

class App extends Component {
    
    
  state = {
    
    
    count: 100,
    carts: [
      {
    
     id: 1, name: 'aa', num: 1 },
      {
    
     id: 2, name: 'bb', num: 2 }
    ]
  }

  addCount = () => {
    
    
    // 原来的写法
    // this.setState(state => ({ count: state.count + 1 }))
    // 使用immer来优化setState操作
    this.setState(
      produce(draft => {
    
    
        // 不用返回,返加不允许,draft它是一个代理对象,修改后,它能监听到数据的变化,更新视图
        draft.count++
      })
    )
  }

  render() {
    
    
    return (
      <div>
        <h3>{
    
    this.state.count}</h3>
        <button onClick={
    
    this.addCount}>++++++</button>
        <hr />
        {
    
    this.state.carts.map((item, index) => (
          <li key={
    
    item.id}>
            <span>{
    
    item.name}</span>
            <span>---</span>
            <span>
              {
    
    item.num} --
              <button
                onClick={
    
    () => {
    
    
                  this.setState(
                    produce(draft => {
    
    
                      // draft它就是当前的this.state
                      draft.carts[index].num++
                    })
                  )
                }}
              >
                ++++
              </button>
            </span>
          </li>
        ))}
      </div>
    )
  }
}

export default App

在这里插入图片描述

1.2 useState结合immer使用

import React, {
    
     useState } from 'react'
import {
    
     produce } from 'immer'

const App = () => {
    
    
  // 普通的数字,不是proxy代理,proxy代理的是对象
  // 如果它是一个普通值,没有必要使用immer来完成优化操作
  let [count, setCount] = useState(100)
  // 对象类型才是使用immer工作的场景
  let [carts, setCarts] = useState([
    {
    
     id: 1, name: 'aa', num: 1 },
    {
    
     id: 2, name: 'bb', num: 2 }
  ])

  return (
    <div>
      <h3>{
    
    count}</h3>
      <button
        onClick={
    
    () => {
    
    
          // 之前的写法
          // setCount(v => v + 1)
          setCount(produce(draft => draft + 1))
        }}
      >
        +++count+++
      </button>
      <hr />
      <button
        onClick={
    
    () => {
    
    
          setCarts(
            produce(draft => {
    
    
              draft.push({
    
     id: Date.now(), num: 1, name: 'aaaa--' + Date.now() })
            })
          )
        }}
      >
        添加商品
      </button>
      {
    
    carts.map((item, index) => (
        <li key={
    
    item.id}>
          <span>{
    
    item.name}</span>
          <span>---</span>
          <span>
            {
    
    item.num} --
            <button
              onClick={
    
    () => {
    
    
                setCarts(
                  produce(draft => {
    
    
                    // 不能返回,写上去感觉就像在vue的感觉
                    draft[index].num++
                  })
                )
              }}
            >
              ++++
            </button>
          </span>
          <span
            onClick={
    
    () => {
    
    
              setCarts(
                produce(draft => {
    
    
                  draft.splice(index, 1)
                })
              )
            }}
          >
            删除
          </span>
        </li>
      ))}
    </div>
  )
}

export default App

在这里插入图片描述

1.3 immer和redux集合

redux/index.js:

import {
    
     createStore } from 'redux'
import {
    
     produce } from 'immer'

const initState = {
    
    
  count: 100
}

// 以前的写法
/* const reducer = (state = initState, { type, payload }) => {
  if ('add' === type) {
    // 深复制
    return { ...state, count: state.count + payload }
  }
  return state
} */

const reducer = produce((draft, {
     
      type,  payload }) => {
    
    
  // 写法就和vuex中的mutation中的写法一样的,简化了
  // 操作数据无需深复制,提升性能
  if ('add' === type) draft.count += payload
}, initState)

export default createStore(reducer)

前端页面:

import React from 'react'
import {
    
     useSelector, useDispatch } from 'react-redux'

const App = () => {
    
    
  const dispatch = useDispatch()
  const count = useSelector(state => state.count)
  return (
    <div>
      <h3>{
    
    count}</h3>
      <button
        onClick={
    
    () => {
    
    
          dispatch({
    
     type: 'add', payload: 2 })
        }}
      >
        ++++
      </button>
    </div>
  )
}

export default App

在这里插入图片描述

2. Redux Toolkit

概述:

它开箱即用的高效 Redux 开发工具集,是 redux 新的库,也是官方推荐今后在项目中使用的 redux 库,内置了immer、redux-thunk和redux-devtools。

安装:

yarn add @reduxjs/toolkit react-redux

使用:

前台页面:

import React from 'react'
import {
    
     useSelector, useDispatch } from 'react-redux'

const App = () => {
    
    
  const dispatch = useDispatch()
  const count = useSelector(state => state.count)
  return (
    <div>
      <h3>{
    
    count}</h3>
      <button
        onClick={
    
    () => {
    
    
          dispatch({
    
     type: 'add', payload: 2 })
        }}
      >
        ++++
      </button>
    </div>
  )
}

export default App

redux入口文件:

import {
    
     configureStore } from '@reduxjs/toolkit'
// 上来它就是分模块,在项目中,所以的数据一定是分模块来管理的

import count from './modules/count'
import user from './modules/users'

export default configureStore({
    
    
  reducer: {
    
    
    count,
    user
  }
})

同步操作(count.js):

import {
    
     createSlice } from '@reduxjs/toolkit'

// 同步操作
const countSlice = createSlice({
    
    
  // 命名空间名称,比redux中更好,redux没有
  // 它的名称要和入口文件中configureStore中的reducer配置对象中的key名称要一致
  name: 'count',
  // 初始化数据源
  initialState: {
    
    
    num: 10 
  },
  // 修改数据源的方法集合
  reducers: {
    
    
    // 组件中派发dispatch(setNum(2)) // 2就会给payload
    // state它就是proxy对象[immer]
    setNum(state, {
     
      payload }) {
    
    
      state.num += payload
    }
  }
})

// 导出给在组件中调用
export const {
    
     setNum } = countSlice.actions

// 把当前模块的reducer导入,集合到大的reducer中
export default countSlice.reducer

异步操作(users.js):

import {
    
     createSlice, createAsyncThunk } from '@reduxjs/toolkit'

// 方案2
// 写在上面,进行网络请求
export const fetchUser = createAsyncThunk('user/fetchUser', async page => {
    
    
  // 实现异步数据获取
  let ret = await (await fetch('/api/users?page=' + page)).json()
  // return 中的数据就是返回到state中的users中的数据  一定要return
  return ret.data
})

// 异步操作
const userSlice = createSlice({
    
    
  name: 'user',
  initialState: {
    
    
    users: []
  },
  reducers: {
    
    
    setUsers(state, {
     
      payload }) {
    
    
      state.users = payload
    }
  },
  // 解决异步
  extraReducers: builder => {
    
    
    // 模拟了promise的3个状态,只取成功状态 fulfilled
    // payload中的数据就是fetchUser方法它return出来的数据
    builder.addCase(fetchUser.fulfilled, (state, {
     
      payload }) => {
    
    
      state.users = payload
    })
  }
})

export const {
    
     setUsers } = userSlice.actions

// 网络请求  --- 内置redux-thunk所以,就可以在此处完成异步操作  -- 我推荐的
// 方案1
export const fetchThunkUser = () => async dispatch => {
    
    
  let ret = await (await fetch('/api/users')).json()
  dispatch(setUsers(ret.data))
}

export default userSlice.reducer

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45605541/article/details/127378608