Реагировать на анти-шаблон для всплывающих окон - PullRequest
0 голосов
/ 12 февраля 2020

Недавно я обнаружил, что реализую шаблон на основе ссылок, который, кажется, go против совета по документации реагирования .

Шаблон выглядит следующим образом:

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}/>
    )
}

Теперь я не делаю вышеизложенного, потому что меня беспокоит утечка абстракции и мой родительский компонент должен обрабатывать как состояние, которым он был создан для манипуляции, так и это состояние всплывающего окна.

Мое мнение состоит в том, что всплывающее окно является прерыванием потока навигации и поэтому должно обрабатываться в своем собственном контексте.

Я ставлю ловушку для своего будущего я с этим (анти) паттерном?

1 Ответ

1 голос
/ 12 февраля 2020

Я одобряю второе, более "React-y", решение, потому что:

  • Имеет смысл, что ваш родитель поддерживает показанное состояние. В более поздних случаях вы, возможно, будете очень рады иметь больше гибкости в отношении всплывающего потока управления.
  • Ваш исходный шаблон гораздо сложнее для чтения и обслуживания, поскольку вы предоставляете императивный API, который менее очевиден и обычно требует дополнительных документация. Вместо этого реквизиты являются простым стандартным React, гораздо более предсказуемы и тестируемы.
  • Ссылки заставляют родителей / пользователей обходить жизненный цикл 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}/>
    )
}

Простой, функциональный, идиоматический c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...