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会更优雅。

相关推荐
熊的猫8 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
速盾cdn15 分钟前
速盾:vue的cdn是干嘛的?
服务器·前端·网络
四喜花露水1 小时前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy1 小时前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
web Rookie1 小时前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust2 小时前
css:基础
前端·css
帅帅哥的兜兜2 小时前
css基础:底部固定,导航栏浮动在顶部
前端·css·css3
yi碗汤园2 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
就是个名称2 小时前
购物车-多元素组合动画css
前端·css
编程一生2 小时前
回调数据丢了?
运维·服务器·前端