React Hooks useEffect中多个依赖项更新问题

3,143 阅读3分钟

问题描述

最近在用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 (
    <div> 
     {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}
      />
    </div>
  )}
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库

// 用于处理useEffect多个依赖批量操作时,数据防抖,获取最新的返回
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…