弹出式参考反样式

问题描述 投票:0回答:1

最近,我发现自己实现了一个基于ref的模式,似乎与react documentation advice背道而驰。

模式如下所示:

type Callback = () => void;
type CallbackWrapper = {callback : Callback}

interface IWarningPopupRef{
    warn : (callback : Callback) => void;
}

interface IWarningPopupProps{
    warningText : string;
}

const WarningPopup = forwardRef<IWarningPopupRef, IWarningPopupProps>(
    const [show, setShow] = useState(false);
    const [callback, setCallback] = useState<CallbackWrapper | null>(null);
    const warn = (callback : Callback) => {
        setShow(true);
        setCallback({callback});
    }
    const acceptWarning = () => {
        setShow(false);
        setCallback(null);
        if(callback != null) callback.callback();
    }
    useImperativeHandle(ref, () => ({
        warn
    }));
    (props, ref) => {
        return (
            <div style={{
                visibility:(show)?"visible":"hidden"
            }}>
                {props.warningText}
                <button onClick={acceptWarning}>Accept</button>
            </div>
        )
    }
)

const Component : React.FC = props => {
    const warningPopupRef = useRef<IWarningPopupRef>(null);
    const doDangerButton = () => {
        warningPopupRef.current!.warn(() => {
            doDangerAction();
        });
    }
    return (
        <button onClick={doDangerButton}>Dangerous button</button>
        <WarningPopup ref={warningPopupRef} 
            warningText="Warning ! This is a dangerous button !"/>
    )
}

如果我要遵循反应文档建议并将状态提升到父组件,我将拥有这个:

interface IWarningPopupProps{
    warningText : string;
    show : boolean;
    onWarningAccept : () => void;
}

const WarningPopup : React.FC<IWarningPopupProps> = props => {
    return (
        <div style={{
            visibility:(props.show)?"visible":"hidden"
        }}>
            {props.warningText}
            <button onClick={props.onWarningAccept}>Accept</button>
        </div>
    )
} 

const Component : React.FC = props => {
    const [warningPopupShow, setWarningPopupShow] = useState(false);
    const doDangerButton = () => {
        setWarningPopupShow(true);
    }
    const acceptWarning = () => {
        setWarningPopupShow(false);
        doDangerAction();
    }
    return (
        <button onClick={doDangerButton}>Dangerous button</button>
        <WarningPopup warningText="Warning ! This is a dangerous button !"
            show={warningPopupShow}
            onWarningAccept={acceptWarning}/>
    )
}

现在,我不执行上述操作,因为我担心抽象泄漏,并且我的父组件必须同时处理创建时要操纵的状态和此弹出状态。

我的理由是,弹出窗口是导航流的中断,因此应在其自身的上下文中进行处理。

我是否使用这种(反)模式为自己的未来自我陷阱?

reactjs typescript design-patterns anti-patterns
1个回答
1
投票

我批准第二个,更多的“ React-y”解决方案,因为:

  • 您的父母保持所显示的状态很有意义。在以后的使用中,您可能会非常乐意在弹出控件流方面具有更大的灵活性。
  • 您的原始模式在暴露和显示命令式API时要阅读和维护起来要复杂得多,该命令不太明显,通常需要其他文档。相反,道具是简单的标准React,更具可预测性和可测试性。
  • 引用通过迫使父母/用户在预期的时间检查ref是否包含元素来强加父母/用户绕过React的生命周期。

最后,您可以通过一种方便使用的方式来调整弹出窗口:

const Component : React.FC = props => {
    const [warningPopupShow, setWarningPopupShow] = useState(false);
    return (
        <button onClick={doDangerButton}>Dangerous button</button>
        <WarningPopup warningText="Warning ! This is a dangerous button !"
            show={warningPopupShow}
            onShow={setWarningPopupShow} // Simply separate "shown" updates from
                                         // acceptation action in the popup
            onWarningAccept={doDangerAction}/>
    )
}

简单,实用,惯用。

© www.soinside.com 2019 - 2024. All rights reserved.