Antd Table组件,state改变,但是render并不会重新渲染

背景

在table上面,当鼠标放在cell上面的时候,需要去请求接口拉取数据,然后setList(res.result)后,希望render中的traceIds也能够实时更新渲染。在这里插入图片描述

const [traceIds, setTraceIds] = useState() // 需要展示在popover上面的数据,接口返回

const renderErrorCodeList = useMemoizedFn((errors: MttkError[], record: MttkInfo) => {
  return (
    <ul className="table-error-code-ul">
      {errors?.length
        ? errors?.map((err) => {
            const msg = err.error_message?.map((e) => e.message)?.join(';');
            return (
              <li key={err.code}>
                <Popover
                  placement="top"
                  content={{loading?'loading...':traceIds}} // 关键点,当list改变的时候,这里的popover并不会重新渲染
                  trigger="hover"
                  title="Detail"
                  overlayClassName="trace-detail-popover"
                  overlayStyle={{ width: 500 }}
                >
                  <a
                    onMouseEnter={() => {
                      // 避免鼠标来回滑入滑出,造成接口的调用
                      // 希望用户能够将鼠标停留在链接上面之后,再唤起接口和popover
                      timer.current = setTimeout(() => {
                        // 获取接口数据...
                        setTraceIds(res.data);
                      }, 1000);
                    }}
                  >
                    {`${err.code}${msg ? `(${msg})` : ''}`}
                  </a>
                </Popover>
              </li>
            );
          })
        : '-'}
    </ul>
  );
});

const columns = [
  {
    title: 'Top Error Codes',
    dataIndex: 'top_error',
    width: 500,
    render: (errors: MttkError[], record: MttkInfo) => renderErrorCodeList(errors, record),
  },
  {
    title: 'QPS',
    dataIndex: 'qps',
  },
];

<Table
  columns={columns}
  dataSource={data}
  size="small"
/>

原因:state改变并不会触发render重新渲染。

参考:https://github.com/ant-design/ant-design/issues/35820
https://github.com/ant-design/ant-design/issues/418

解决方法

(1)考虑前端自定义一个变量list,当接口返回数据后,我们手动修改datasource的数据
(2)利用 shouldCellUpdate 实时更新当前单元格

export default function InfoTable(props: Props) {
  const { dataSource, timestamp, azKey } = props;
  const timer = useRef<number>();
  const [data, setData] = useState<MttkInfo[]>([]); // datasource

  const jumpToTracingPage = (traceId: string) => {
    window.open(`${basePath}/tracing?traceId=${traceId}`);
  };

  const { run: getTraceList } = useDebounceFn(
    async (code: string, record: MttkInfo, latency?: number) => {
      try {
          // 请求接口...
          const res = await getTraceIdListByCode(params);

          const newData = data?.map((info) => {
            if (info.id === record.id) {
              return { ...info, traceIds: res?.data, isCallEnd: Math.random() }; // 关键点
            }
            // 对于没有命中的record,重置traceIds,方便判断是否展示 loading
            return { ...info, traceIds: undefined };
          });

          setData(newData);
        }
      } catch (err) {
        console.error(err);
      }
    },
    {
      wait: 500,
    },
  );

  const getErrorInfo = (error: MttkError) => {
    return <div>其他固定信息...</div>
  };

  const renderTraceListContent = (traceIds?: string[]) => {
    let content: ReactNode = '';

// 关键点:判断状态
    if (traceIds === undefined) {
      content = 'Loading...';
    } else if (traceIds?.length === 0) {
      content = 'Not found';
    } else {
      content = (
        <ul style={{ paddingLeft: 15 }}>
          {traceIds?.map((id) => (
            <li key={id}>
              <a onClick={() => jumpToTracingPage(id)}>{id}</a>
            </li>
          ))}
        </ul>
      );
    }

    return content;
  };

  const renderErrorCodeList = useMemoizedFn((errors: MttkError[], record: MttkInfo) => {
    const { traceIds = undefined } = record;
    const content = renderTraceListContent(traceIds); // 获取trace id列表

    return (
      <ul className="table-error-code-ul">
        {errors?.length
          ? errors?.map((err) => {
              const baseInfo = getErrorInfo(err);
              const msg = err.error_message?.map((e) => e.message)?.join(';');
              return (
                <li key={err.code}>
                  <Popover
                    placement="top"
                    content={
                      <>
                        {baseInfo}
                        <div style={{ fontSize: 12 }}>{content}</div>
                      </>
                    }
                    trigger="hover"
                    title="Detail"
                    overlayClassName="trace-detail-popover"
                    overlayStyle={{ width: 500 }}
                  >
                    <a
                      onMouseEnter={() => {
                        // 避免鼠标来回滑入滑出,造成接口的调用
                        // 希望用户能够将鼠标停留在链接上面之后,再唤起接口和popover
                        timer.current = setTimeout(() => {
                          getTraceList(err.code, record);
                        }, 1000);
                      }}
                      onMouseLeave={() => {
                        clearTimeout(timer.current);
                        // 对于同一个单元格,如果有多个code,重复点击同一个单元格的时候,会造成一直显示上次的内容。所以在鼠标移出后,需要重置traceIds为undefined
                        // 或者同一行来回移动,popover还是会保持上一次的内容
                        setData(data?.map((info) => ({ ...info, traceIds: undefined })));
                      }}
                    >
                      {`${err.code}${msg ? `(${msg})` : ''}`}
                    </a>
                  </Popover>
                </li>
              );
            })
          : '-'}
      </ul>
    );
  });

  const columns = [
    {
      title: 'Top Error Codes',
      dataIndex: 'top_error',
      width: 500,
      render: (errors: MttkError[], record: MttkInfo) => renderErrorCodeList(errors, record),
      shouldCellUpdate: (record: MttkInfo, pre: MttkInfo) => record?.isCallEnd !== pre?.isCallEnd, // 关键点,表示请求结束了,需要重新渲染当前单元格
    },
    {
      title: 'QPS',
      dataIndex: 'qps',
    },
  ];
  return (
    <Table
      columns={columns}
      dataSource={data}
      size="small"
    />
  );
}

但是上面的处理方法存在一个问题:

(1)如果在同一行来回移动,popover展示的是上一次的信息
(2)如果在当前单元格内上下移动(比如有多个li元素),popover展示的是上一次的信息

希望能够找到优化方式…


或者考虑使用 useUpdate hooks,在需要setState重新渲染的时候,调一下它??但是会造成表格的所有数据都被重新渲染

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值