React 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
到此这篇关于React immer与Redux Toolkit使用教程详解的文章就介绍到这了,更多相关React immer与Redux Toolkit内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论