
文章目录

- //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” 文件中。
- //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
- //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>
- );
- }
-

Redux Toolkit 的 createAsyncThunk API 生成 thunk,为你自动 dispatch 那些 "状态" action。
createAsyncThunk 接收 2 个参数:
1、 参数一:将用作生成的 action type的前缀的字符串
2 、一个 “payload creator” 回调函数,它应该返回一个 Promise 或者其他数据
- //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
- 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。
- //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 自动生成 pending/fulfilled/rejected action 类型
- //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
- })
- }
- //pages/Product.js
- import { addProductPost, productListGet } from '../slices/productSlice'
- //组件挂载后请求商品数据
- useEffect(() => {
- dispatch(productListGet())
- }, [])
提示:
Immer 让我们以两种方式更新状态:要么 更新 现有状态值,要么 return 一个新结果。
如果我们返回一个新值,它将用我们返回的任何内容完全替换现有状态。

React.memo()
React 的默认行为是当父组件渲染时,React 会递归渲染其中的所有子组件!
- //pages/ProductChild.js
- import React, { useEffect } from 'react';
- function ProductChild() {
-
- useEffect(() => {
- console.log('子元素重新渲染')
- })
- return (
- <div>
- 子元素
- div>
- );
- }
- export default React.memo(ProductChild)
-
为了让子组件跳过没有必要的渲染,我们可以将 子组件包装在 React.memo() 中,这可以确保组件只有在 props 真正更改时才会重新渲染。
- //pages/ProductChild.js
- export default React.memo(ProductChild)

如果子组件中使用了useSelector来获取数据,也会存在一些不必要的渲染。
提示:
一般情况下,只要我们dispatch 了 action,store发生了变更之后,那么传递给useSelector的选择器器就会被重新调用,如果选择器返回的结果跟原来的状态不一样,则组件重新被渲染。
- 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 来定义有记忆的选择器。
- //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个输入函数的返回值决定。只有当输出函数的参数发生了变更,输出函数才会被重新执行。
- //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)