react-hooks icon indicating copy to clipboard operation
react-hooks copied to clipboard

feat(control): Introduce control hooks

Open 599316527 opened this issue 3 years ago • 1 comments

目标

通过hooks简化弹窗类组件的调用。

现状

实现弹窗交互时我们通常写出这样的代码。当组件需求越来越复杂,或者一个页面中出现多个弹窗时,这样的写法使得组件内 state 满天飞,可维护性急剧下降。同时,打开弹窗操作所引起的 state 更新会导致整个组件刷新,照成性能问题。

function Demo() {
    const [editId, setEditId] = useState();
    const [editField, setEditField] = useState();
    const EditorDialog = fieldToEditorMap[editField];

    const openEditor = useCallback((id, field) => {
        setEditId(id)
        setEditField(field)
    }, [setEditId, setEditField]);

    return <>
        <MassiveTable>
            ...
            <SomeButton onClick={() => openEditor(xxxId, field)} />
            ...
        </MassiveTable>
        {EditorDialog && <EditorDialog id={editId} onClose={() => setEditField(undefined)} />}
    </>;
}

解决

把弹窗相关逻辑独立成组件提升可维护性。并且通过 useControluseControlSource hook 来关联两者,实现简单易用。

function Demo() {
    const [ControlledEditor, {open: openEditor}] = useControl(Editor);

    return <>
        <MassiveTable>
            <SomeButton onClick={() => openEditor(xxxId, field)} />
        </MassiveTable>
        <ControlledEditor />
    </>;
}

function Editor(props, ref) {
    const [[editId, editField], {close}] = useControlSource(ref, (setState) => ({
        open: (id, field) => setState([id, field]),
        close: () => setState([])
    }), []);
    const EditorDialog = fieldToEditorMap[editField];
    {EditorDialog && <EditorDialog id={editId} onClose={close} />}
}

这样无论是直接使用 open 还是把它通过 context 往下传递,调用它开关弹窗都不会导致其它地方的无意义刷新。

目前在业务代码里用起来还挺方便的,所以感觉可以放到 huse 里面。

更详细的例子可以看 demo 实现。

599316527 avatar Nov 12 '21 14:11 599316527

我终于看懂了……核心是利用ref跨组件地传递一些命令式的功能,这个思路很棒

具体到类型的完备性和命名,我再仔细推敲一下

otakustay avatar Nov 15 '21 05:11 otakustay