最近在用hooks写公司的项目,发现一个问题,在useEffect的数组依赖项多个,并且同时更新以后,会出现结果返回不稳定,不是最新的数据。
useEffect(() => {
setTableListData();
if (stateDate && endDate) getData(PAGINATION);
}, [toggleTabData, startDate, endDate, inputId]);
比如上面的useEffect中依赖了切换tab,开始时间,结束时间和input的值,比如这个样子。
需求就是,在我tab切换的时候需要去把时间及input都进行重置成默认值以后请求数据,更新表格的最新数据,部分代码如下:
import React, { useState, useEffect } from 'react'
import StatusCard from './StatusCard'
import DateRange from './DateRange'
const tradeList = () => {
const [toggleTabData, setToggleTabData] = useState()
const [startDate, setStartDate] = useState()
const [endDate, setEndDate] = useState()
const [inputId, setInputId] = useState()
const [tableListData, setTableListData] = useState()
const [dateRange, setDateRange] = useState()
const columns = [
title: '姓名',
dataIndex: 'name',
key: 'name',
title: '年龄',
dataIndex: 'age',
key: 'age',
title: '住址',
dataIndex: 'address',
key: 'address',
const getData = () => {
console.log(toggleTabData, startDate, endDate, inputId)
useEffect(() => {
getData()
}, [toggleTabData, startDate, endDate, inputId])
useEffect(() => {
setStateDate(moment(dateRange.date[0]).valueOf())
setEndDate(moment(dateRange.date[1]).valueOf())
}, [dateRange])
return (
{Object.keys(tabStatusData).length > 0 && (
<StatusCard tabList={tabStatusData} onClickTab={toggleTab} />
<InputNumber
style={{ width: '200px' }}
onChange={handleInputChange}
value={inputId}
placeholder={formatMessage({
id: 'm-tmall_TradeRecord_Search_Placeholder_Input',
defaultMessage: 'input id',
allowClear
<DateRange
className={styles.inputid}
dateRange={setDateRange}
defaultDateRange={dateRange}
<Table
rowSelection={rowSelection}
columns={columns}
dataSource={tableListData}
pagination={pagination}
loading={loading}
onChange={getData}
export default tradeList
当我们选择一个任一时间后切换tab,希望的效果是,时间重置,且input值清空,然后用重置的时间和最新的tab去请求回最新数据,然而会偶发性实现,或者数据为旧数据。
原因是useEffect在通过依赖数组去更新数据时,只要是在更新的数据都会并发的去发同时的更新状态,而返回结果的顺序不能固定,比如说,数据识别到toggleTabData,startDate,endDate都同时请求更新的时候会发出三条更新的并发:
1.toggleTabData, 旧的startDate,旧的endDate
2.toggleTabData, 新的startDate,旧的endDate
3.toggleTabData, 新的startDate,新的endDate
而这三条线其实是同时进行的,所以返回的顺序其实是不能如我们需要返回最新的第三条线的数据。所以要实现的话,有两种方法
1、单独写toggleTabData的useEffect,并把startDate和endDate的默认值传进去。(不推荐)
const getData = (front, end) => {
const newStartDate = front ? front : startDate
const newEndDate = end ? end : endDate
console.log(toggleTabData, newStartDate, newEndDate, inputId)
} useEffect(() => {
const front = moment()
const end = moment()
getData(front, end)
}, [toggleTabData])
useEffect(() => {
getData()
}, [startDate, endDate, inputId])
弊端:首次请求的时候会出现两次getData的调用。
2、合并state,将toggleTabData, startDate, endDate, inputId需要同时请求的合并成一个state。(不推荐)
const [type, setType] = useState({
toggleTabData: '',
startDate: '',
endDate: '',
inputId: '',
这样就不会请求两次,但是逻辑不合理,因为每一个功能点不一样,后续维护难度直线上升。
3、通过数据防抖的原理只获取最新的数据。(推荐)
其实这多次并发后,就是类似于防抖功能,只实现最后一次内容的一个逻辑,借用一下lodash-es库
import { useRef, useEffect } from 'react';
import { debounce } from 'lodash-es';
const useDebounce = (fn: T, wait = 1000) => {
const func = useRef(fn); func.current = fn;
const debounceWrapper = useRef(debounce((args) => func.current?.(args), wait));
return debounceWrapper.current as unknown as T;}
export function useBatchEffect(effect: any, deps?: any, wait = 0) {
const fn = useDebounce(effect, wait); useEffect(fn, deps);
useBatchEffect(() => {
getData()
}, [toggleTabData, startDate, endDate, inputId])
这样就实现了我们所需要的效果啦~
文章参考来自www.jianshu.com/p/ca6765bbd…