文章目录
异步逻辑
javascript
//Product.js
const onAdd = () => {
const name = nameRef.current.value
// 触发添加商品的事件
dispatch(addProduct({name}))
}
如果要让异步逻辑与Store交互,我们需要使用redux middleware。
Redux 有多种异步 middleware,每一种都允许你使用不同的语法编写逻辑。最常见的异步 middleware 是 redux-thunk ,它可以让你编写可能直接包含异步逻辑的普通函数。
Redux Toolkit 的 configureStore 功能默认自动设置 thunk middleware,我们推荐使用 thunk 作为 Redux 开发异步逻辑的标准方式。
Thunk 函数
在Thunk函数中我们可以编写异步逻辑的代码(例如 setTimeout 、Promise 和 async/await ),并且可以通过参数获取到dispatch,getState()。从而在异步操作执行后再diapacth action。
提示:
Thunk 通常写在 "slice" 文件中。
javascript
//slices/productSlice.js
import { createSlice } from '@reduxjs/toolkit'
//定义初始state
//list表示商品列表,isLoading表示是否为正在请求数据的状态
const initialState = { list: [] ,isLoading:false}
//创建slice
const slice = createSlice({
//定义域名称
name: 'product',
//传入初始state
initialState,
//定义reducers
reducers: {
//这个reducer用来把商品数据存储到store中
addProduct: (state, action) => {
state.list.push(action.payload)
},
//这个reducer用来更改isLoading
changeState:(state,action)=>{
state.isLoading=action.payload
}
}
})
//导出action creator
export const { addProduct ,changeState} = slice.actions
//导出thunk函数
//addProductAsync为thunk函数的创建函数,它返回一个thunk函数
//返回的thunk函数中我们就可以编写异步代码了
export const addProductAsync = (payload) => (dispatch, getState) => {
//触发action ,改变isLoading的状态
dispatch(changeState(true))
setTimeout(() => {
dispatch(addProduct(payload))
//触发action ,改变isLoading的状态
dispatch(changeState(false))
}, 3000)
}
//导出reducer
export default slice.reducer
javascript
//pages/Product.js
import React, { useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { addProductAsync } from '../slices/productSlice'
//引入thunk函数
export default function Product() {
const nameRef = useRef()
const {list:productList,isLoading} = useSelector(state => state.product)
const dispatch = useDispatch()
const onAdd = () => {
//thunk函数的使用,跟普通的action creator的使用一样
dispatch(addProductAsync({ name: nameRef.current.value }))
}
return (
<div>
我是商品页面<br />
商品名:<input ref={nameRef} required /><br />
{isLoading?<div>请求数据中... </div>:productList.map((item, index) => <li key={index}>
商品名:{item.name}
</li>)}
<button onClick={onAdd}>新增商品</button>
</div>
);
}
createAsyncThunk(一)
Redux Toolkit 的 createAsyncThunk API 生成 thunk,为你自动 dispatch 那些 "状态" action。
createAsyncThunk 接收 2 个参数:
1、 参数一:将用作生成的 action type的前缀的字符串
2 、一个 "payload creator" 回调函数,它应该返回一个 Promise 或者其他数据
javascript
//slices/productSlice.js
//使用createAsyncThunk创建thunk
//接收的第一个参数是action 的 type的前缀
//第二个参数是一个函数,用来返回payload
export const addProductPost = createAsyncThunk('product/addProductPost', (item)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(item)
},3000)
})
})
提示:
当调用dispatch(addProductPost()) 的时候,addProductPost 这个 thunk 会
首先 dispatch 一个 action 类型为 'product/addProductPost/pending'
当异步代码执行完,返回的Promise resove后会dispatch 一个
action 类型为 product/addProductPost/fulfilled
在组件中 dispatch thunk
javascript
import React, { useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux'
import { addProductPost } from '../slices/productSlice'
//引入thunk函数
export default function Product() {
const nameRef = useRef()
const {list:productList,isLoading} = useSelector(state => state.product)
const dispatch = useDispatch()
const onAdd = () => {
//thunk函数的使用,跟普通的action creator的使用一样
dispatch(addProductPost({ name: nameRef.current.value }))
}
return (
<div>
我是商品页面<br />
商品名:<input ref={nameRef} required /><br />
{isLoading?<div>请求数据中...
</div>:productList.map((item, index) => <li key={index}>
商品名:{item.name}
</li>)}
<button onClick={onAdd}>新增商品</button>
</div>
);
}
添加extraReducers
extraReducers可以监听createAsyncThunk创建的action被 dispatch。
javascript
//slices/productSlice.js
//创建slice
const slice = createSlice({
//定义域名称
name: 'product',
//传入初始state
initialState,
//定义reducers
reducers: {
//这个reducer用来把商品数据存储到store中
addProduct: (state, action) => {
state.list.push(action.payload)
},
//这个reducer用来更改isLoading
changeState:(state,action)=>{
state.isLoading=action.payload
}
},
//extraReducer设置createAsyncThunk创建的thunk被dispatch后的reducer处理器
extraReducers(builder){
builder
.addCase(addProductPost.pending,(state,action)=>{
state.isLoading=true
})
.addCase(addProductPost.fulfilled,(state,action)=>{
state.isLoading=false
state.list.push(action.payload)
})
}
})
createAsyncThunk(二)
提示:
createAsyncThunk 自动生成 pending/fulfilled/rejected action 类型
javascript
//slices/productSlice.js
//创建获取商品数据的thunk
export const productListGet = createAsyncThunk('product/productListGet',
async () => {
const data = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve([{ name: '苹果' }, {name: '香蕉' }, { name: "蓝莓" }])
}, 3000)
})
return data
})
extraReducers(builder) {
builder
.addCase(addProductPost.pending,(state, action) => {
state.isLoading = true
})
.addCase(addProductPost.fulfilled, (state,action) => {
state.isLoading = false
state.list.push(action.payload)
})
.addCase(productListGet.pending, (state, action) => {
state.isLoading = true
})
.addCase(productListGet.fulfilled, (state, action) => {
return { list: action.payload, isLoading: false }
})
.addCase(productListGet.rejected, (state,action) => {
state.isLoading=false
})
}
javascript
//pages/Product.js
import { addProductPost, productListGet } from '../slices/productSlice'
//组件挂载后请求商品数据
useEffect(() => {
dispatch(productListGet())
}, [])
提示:
Immer 让我们以两种方式更新状态:要么 更新 现有状态值,要么 return 一个新结果。
如果我们返回一个新值,它将用我们返回的任何内容完全替换现有状态。
性能优化
React.memo()
React 的默认行为是当父组件渲染时,React 会递归渲染其中的所有子组件!
javascript
//pages/ProductChild.js
import React, { useEffect } from 'react';
function ProductChild() {
useEffect(() => {
console.log('子元素重新渲染')
})
return (
<div>
子元素
</div>
);
}
export default React.memo(ProductChild)
为了让子组件跳过没有必要的渲染,我们可以将 子组件包装在 React.memo() 中,这可以确保组件只有在 props 真正更改时才会重新渲染。
javascript
//pages/ProductChild.js
export default React.memo(ProductChild)
createSelector
如果子组件中使用了useSelector来获取数据,也会存在一些不必要的渲染。
提示:
一般情况下,只要我们dispatch 了 action,store发生了变更之后,那么传递给useSelector的选择器器就会被重新调用,如果选择器返回的结果跟原来的状态不一样,则组件重新被渲染。
javascript
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
function ProductChild() {
const list=useSelector(state=>state.product.list.filter(item=>item.name.length>2))
useEffect(() => {
console.log('子元素重新渲染')
})
return (
<div>
子元素
</div>
);
}
export default React.memo(ProductChild)
我们可以使用createSelector 来定义有记忆的选择器。
javascript
//slices/productSlice.js
import { createSelector} from '@reduxjs/toolkit'
export const selectList= createSelector([state =>{
return state.product.list
} ], (list) => {
console.log('重新计算list')
return list.filter(item=>item.name.length>2)
})
createSelector函数可以接收N-1个输入函数,一个输出函数(最终的选择器),前面的N-1个输入函数的参数由调用输出函数的时候传入的参数决定,输出函数的参数由前面N-1个输入函数的返回值决定。只有当输出函数的参数发生了变更,输出函数才会被重新执行。
javascript
//pages/ProductChild.js
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
import {selectList} from '../slices/productSlice'
function ProductChild() {
// const list=useSelector(state=>state.product.list.filter(item=>item.name.length>2))
const list=useSelector(selectList)
useEffect(() => {
console.log('子元素重新渲染')
})
return (
<div>
子元素
</div>
);
}
export default React.memo(ProductChild)