React immer与Redux Toolkit
月光晒了很凉快 人气:01. 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
加载全部内容