ant-design / pro-components

🏆 Use Ant Design like a Pro!
https://pro-components.antdigital.dev
MIT License
4.24k stars 1.35k forks source link

🐛[BUG]BetaSchemaForm组件的params属性与formRef属性的冲突bug(^v1.1.12) #5603

Closed melodyWxy closed 1 year ago

melodyWxy commented 2 years ago

提问前先看看:

https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md

🐛 bug 描述

嘿,我正在做基于ProComponent封装一套2B的产品解决方案,这显得我很酷。

但在使用BetaSchemaForm组件时,我发现了这样的bug: 当我尝试使用request属性和params属性时,发现当params内含有闭包变量时,会引发formRef的标记丢失。

我仔细排查过,发现使用useState, useRef等返回的闭包变量值时就会引发这个问题。

我做了两个极简的复现demo, 请在下方的复现步骤进行查看:

📷 复现步骤

import React, { useState, useEffect, useRef } from 'react';
import { BetaSchemaForm } from '@ant-design/pro-components';

export default () => {
  const targetRef = useRef();
  const [ requestLibData, setRequestLibData ] = useState(0);
  useEffect(()=>{
    // 更新requestLibData,并引发reRender
    setTimeout(()=>{
      setRequestLibData(1)
    })
  }, [])
  // 查看reRender后的ref标记
  useEffect(()=>console.log('targetRef.current1', targetRef.current))
  return (
    <BetaSchemaForm 
      request={async () => ({})}
      params={{requestLibData}}
      columns={[]}
      formRef={targetRef}
    />)
}

image

🏞 期望结果

两次targetRef.current都应该是有值的。但很遗憾如上图所示,ref标记反馈给我的是null。

💻 复现代码

import { BetaSchemaForm } from '@ant-design/pro-components';
import React, { useState, useEffect, useRef } from 'react';

export default () => {
  const fRef = useRef();
  // 仅用来引发reRender
  const [ renderCount, setRenderCount ] = useState(0);

  const requestLibRef = useRef(0);

  useEffect(()=>{
    setTimeout(()=>{
      // params值更新
      requestLibRef.current = 1
      // reRender
      setRenderCount(1)
    })
  }, [])

  useEffect(()=>console.log('fRef.current1', fRef.current))

  return (
    <BetaSchemaForm 
      request={async () => ({})}
      params={{requestLibData: requestLibRef.current}}
      columns={[]}
      formRef={fRef}
    />)
}

这将引发同样的表现 image

© 版本信息

🚑 其他信息

我通过其他方式绕过这个bug,解决了我的需求。 但这个bug引发了我的兴趣,于是就在刚才,我开始尝试查看源码以便找到它的来源。 然而很遗憾的是,formRef属性的处理逻辑与太多无关代码耦合到了一起,这使得我的追踪遭遇了阻碍。 我将在下面的评论中建议一种代码结构,这无关bug本身。

melodyWxy commented 2 years ago

hi,正如上述提到的,这是一个微不足道的建议,或者说是关于react组件代码结构规范的一些思考。

背景: 我正在基于ProComponent封装更上层的耦合了业务模型的复用业务组件。

我将组件中胶水层代码的对每个属性的处理,都封装成为了独立的hook,每个文件都没有超过150行,这使得我可以轻易的通过独立的功能hook来找到迭代我的功能的代码区块。

确认功能需求 => 确认对应的prop => 找到对应的hook => 迭代它!或者debugger~

于是单一组件中,我的代码结构是这样子的: image

// index.tsx

import React, { useRef, forwardRef, useImperativeHandle, ForwardRefExoticComponent, PropsWithoutRef, RefAttributes } from 'react';

import { ActionType, ProFormInstance, ProTable } from '@ant-design/pro-components';
import { SPFormBase } from '../../../SPForm';

import {
  useAddRecordForm,
  useAddRecordWrap,
  useColumnsProp,
  useEditableProp,
  usePaginationProp,
  useRowKeyProp,
  useSearchProp,
  useToolBarRender,
} from './effect';

import type { XtableBaseProps, XTableRef } from './type';

export const SPTableBase: ForwardRefExoticComponent<PropsWithoutRef<XtableBaseProps> & RefAttributes<XTableRef>> = forwardRef<XTableRef, XtableBaseProps>(
  (
    {
      autoRowEditConfig,
      autoAddRecordConfig,
      editable,
      search,
      pagination,
      toolBarRender,
      request,
      rowKey,
      columns,
      tableBaseMode,
      params,
      ...otherProps
    },
    ref,
  ) => {
    // 内置功能处理 - start
    const actionRef = useRef<ActionType>();
    const queryFormRef = useRef<ProFormInstance>();
    // const effectFormRef = useRef()
    const { addRecordWrapVisible, setAddRecordWrap } = useAddRecordWrap();
    const { mergeRecordFormType, mergeRecordFormColumns } = useAddRecordForm({
      recordFormType: autoAddRecordConfig?.recordFormType,
      columns,
      steps: autoAddRecordConfig?.steps,
    });
    // 内置功能处理 - end

    // 属性封装处理 - start
    const mergeSearch = useSearchProp({ search });
    const mergeColumns = useColumnsProp({ columns, autoRowEditConfig, rowKey, tableBaseMode });
    const mergeEditable = useEditableProp({ editable, actionRef, request, autoRowEditConfig });
    const mergePagination = usePaginationProp({ pagination });
    const mergeRowKey = useRowKeyProp({ rowKey });
    const mergeToolBarRender = useToolBarRender({
      toolBarRender,
      setAddRecordWrap,
      autoAddRecordConfig,
      addRecordWrapVisible,
      tableBaseMode,
    });
    // 属性封装处理 - end

    // ref转发
    useImperativeHandle(ref, () => ({
      tableAction: actionRef,
      queryForm: queryFormRef,
    }));
    return (
      <>
        <ProTable
          formRef={queryFormRef}
          params={params}
          dateFormatter="string"
          editable={mergeEditable}
          search={mergeSearch}
          actionRef={actionRef}
          pagination={mergePagination}
          cardBordered
          toolBarRender={mergeToolBarRender}
          request={request}
          columns={mergeColumns}
          rowKey={mergeRowKey}
          {...otherProps}
        />
        <SPFormBase
          layoutType={mergeRecordFormType}
          columns={mergeRecordFormColumns}
          visible={addRecordWrapVisible}
          drawerProps={
            mergeRecordFormType === 'DrawerForm'
              ? {
                  title: autoAddRecordConfig?.formTitle || '新增',
                  destroyOnClose: true,
                  onClose: () => setAddRecordWrap(false),
                }
              : undefined
          }
          modalProps={
            mergeRecordFormType === 'ModalForm'
              ? {
                  title: autoAddRecordConfig?.formTitle || '新增',
                  destroyOnClose: true,
                  onCancel: () => setAddRecordWrap(false),
                }
              : undefined
          }
          onFinish={async (values) => {
            await autoAddRecordConfig?.onSave(values);
            setAddRecordWrap(false);
            actionRef.current?.reload();
          }}
        />
      </>
    );
  },
);

export default SPTableBase;

或许贵团队有更好的思考和规范,但这仅仅是个建议,期望带给大家更多的思考~

melodyWxy commented 1 year ago

有木有童鞋处理一下呀。

xXAvoraXx commented 1 year ago

Is this problem fixing?

melodyWxy commented 1 year ago

Is this problem fixing?

no

laosandegudai commented 1 year ago

有木有童鞋处理一下呀。

请问是如何处理这个问题的,我目前也是发现使用了request后就会出现formRef无法使用,去掉request后就正常了。