React官方不推荐这样使用useImperativeHandle,我偏要用,和官方对着干!

前言

React.useImperativeHandle 不知道这个hook你们使用的频率如何,我反正是经常用在业务的Modal组件上。今天查看react新的官方文档发现,我这种使用方式居然是react官方不推荐的,这让我不禁思考起来"难道我一直都踩在陷阱里?"

再仔细翻阅文档,我并没有详细地看到官方告诉我,我这种用法到底有什么问题?单凭下面几句无法说服我,我反而觉得ModalRef.open()这种写法更加优雅。

我的写法

在以下代码案例中,我举例一个Modal组件,通过useImperativeHandle向外expose了一个方法open。 该方法中接受一个传参id(而不是通过props传入),同时执行了request等副作用。

tsx 复制代码
const _IssueModal = (props: IProps, ref: Ref<IRef>) => {
    //变量声明、解构
    const {} = props;
    const { issueApi, roomTreeApi } = settingScreen$;
    //组件状态
    const uiIdRef = useRef('');
    const deviceIdsRef = useRef<string[]>([]);
    const [open, setOpen] = useState(false);

    //网络IO

    //数据转换
    const options = transformOptions(roomTreeApi.data || []);

    //逻辑处理函数
    const handleIssue = async () => {
        issueApi.request();
    };
    const handleReset = () => {
        uiIdRef.current = '';
        deviceIdsRef.current = [];
        setOpen(false);
    };

    //组件Effect
    useImperativeHandle(ref, () => ({
        open: (id: string) => {
            if (!id) return;
            uiIdRef.current = id;
            setOpen(true);

            roomTreeApi.request();
        },
    }));

    //组件渲染
    return (
        <Modal
            open={open}
            title='主题资源包下发'
            onCancel={handleReset}
            onOk={handleIssue}
            destroyOnClose
            maskClosable={false}
            okButtonProps={{
                disabled: !deviceIdsRef.current.length,
            }}
        >
            <p>请选择要更新的设备</p>
            <Cascader
                className={styles.CascaderSelect}
                options={options}
            />
        </Modal>
    );
};

//props类型定义
interface IProps {}

interface IRef {
    open: (id: string) => void;
}

//prop-type定义,可选
const IssueModal = observer<IProps, IRef>(_IssueModal, { forwardRef: true });
export { IssueModal };

使用ModalRef.open的好处

  1. 可读性好

我把相关的业务逻辑与state,都封装在一个单独的组件里,而不是把所有逻辑平铺到父组件。 特别是当前业务场景有多个Modal,如果采用传统写法的代码就像拉面条,每个open状态还需要单独命名,相当痛苦且不美观。

  1. 解耦

父组件不需要知道我这个Modal是干什么的,也不应该直接修改Modal的内部状态,只需要按照声明的接口IRef调用open就可以。

  1. 复用

上述例子中,IssueModal是个具有完整业务逻辑的组件,其他地方需要复用这段逻辑,直接引用Ref就可以。

tsx 复制代码
    // 使用React.ComponentRef 获取IRef类型,不要再写any了!!!
    const issueModalRef = useRef<ComponentRef<typeof IssueModal>>(null);
    // ...
    const handleClick = (record) => {
        issueModalRef.current?.open(record.id);
    }
    // ...
    <IssueModal ref={issueModalRef} />

同道中人

google了一下,发现了不少人也和我一样是这样写Modal组件的。举个几个例子:

后话

这篇文章并不是说我的写法是那么牛逼,而是解答我心中疑惑,"React官方推荐的一定是对吗?有没有更好的写法?当面对不同的业务场景我应该选择哪种写法会更好?"

感谢React官方提供那么棒的前端开源框架,只是基于OOP的开发思维&经验,面对我的业务场景时,我觉得使用useImperativeHandle会更优雅。

相关推荐
dream_ready42 分钟前
linux安装nginx+前端部署vue项目(实际测试react项目也可以)
前端·javascript·vue.js·nginx·react·html5
编写美好前程43 分钟前
ruoyi-vue若依前端是如何防止接口重复请求
前端·javascript·vue.js
flytam44 分钟前
ES5 在 Web 上的现状
前端·javascript
喵喵酱仔__1 小时前
阻止冒泡事件
前端·javascript·vue.js
GISer_Jing1 小时前
前端面试CSS常见题目
前端·css·面试
八了个戒1 小时前
【TypeScript入坑】什么是TypeScript?
开发语言·前端·javascript·面试·typescript
不悔哥1 小时前
vue 案例使用
前端·javascript·vue.js
anyup_前端梦工厂2 小时前
Vuex 入门与实战
前端·javascript·vue.js
你挚爱的强哥2 小时前
【sgCreateCallAPIFunctionParam】自定义小工具:敏捷开发→调用接口方法参数生成工具
前端·javascript·vue.js
米老鼠的摩托车日记3 小时前
【vue element-ui】关于删除按钮的提示框,可一键复制
前端·javascript·vue.js