Ловушка Фокуса React и переиспользование компонентов в 2 модальных окнах
У меня есть 2 модальных окна(я переиспользую в обоих CloseButton(компонент) and Modal(компонент) в обоих) мне нужно сделать ловушки фокуса в обоих. Ищу варианты наилучшего подхода + другие варианты + не могу понять в чем сам ошибаюсь = хочу разобраться.
1 popup [наглядно показал его структуру], компоненты: ModalLogin-Modal-CloseButton. Читал по поводу 2 хуков: useRef() and forwardRef(props, ref) пробую их юзнуть и ошибки. Я ищу ответ как их правильно применить (если это возможно) или как поступить лучше будет + почему :)
В ModalLogin я пробую сделать ловушку фокуса. Для этого я размечаю что должно происходить с фокусом при переходе на 1 и последний элемент. Мне нужно передать мой ref-хук получается через Modal-CloseButton. Читал что нельзя передавать просто в функциональные компоненты рефы. Пробую юзать форвардреф-хук в необходимых компонентах куда передаю, вот что делаю:
Все линки дают полное представление о всем происходящем в компоненте до ловушки фокуса (стараюсь не засорять сильно пост).
https://github.com/j3n4r3v/ligabank-credit/blob/master/src/components/form-login/modal-login.jsx [Modal-login full]
const ModalLogin = () => {
const topTabTrap* = useRef();
const bottomTabTrap* = useRef();
const firstFocusableElement = useRef();
const lastFocusableElement = useRef();
useEffect(() => {
const trapFocus = (event) => {
if (event.target === topTabTrap.current) {
lastFocusableElement.current.focus()
}
if (event.target === bottomTabTrap.current) {
firstFocusableElement.current.focus()
}
}
document.addEventListener('focusin', trapFocus)
return () => document.removeEventListener('focusin', trapFocus)
}, [firstFocusableElement, lastFocusableElement])
return (
<Modal onCloseModal={() => onCloseForm()} ref={lastFocusableElement}>
<form >
<span ref={topTabTrap} tabIndex="0" />
<Logo />
<Input id="email" ref={firstFocusableElement} />
<Input id="password" />
<Button type="submit" />
<span ref={bottomTabTrap} tabIndex="0"/>
</form>
</Modal>
https://github.com/j3n4r3v/ligabank-credit/blob/master/src/components/modal/modal.jsx [Modal full]
const Modal = forwardRef(({ props, ref }) => {
const { children, onCloseModal, ...props } = props;
const overlayRef = useRef();
useEffect(() => {
const preventWheelScroll = (evt) => evt.preventDefault();
document.addEventListener('keydown', onEscClick);
window.addEventListener('wheel', preventWheelScroll, { passive: false });
return () => {
document.removeEventListener('keydown', onEscClick);
window.removeEventListener('wheel', preventWheelScroll);
};
});
const onCloseModalButtonClick = () => {
onCloseModal();
};
return (
<div className="overlay" ref={overlayRef}
onClick={(evt) => onOverlayClick(evt)}>
<div className="modal">
<CloseButton
ref={ref}
onClick={() => onCloseModalButtonClick()}
{...props}
/>
{children}
</div>
</div>
);
});
https://github.com/j3n4r3v/ligabank-credit/blob/master/src/components/close-button/close-button.jsx [CloseButton full]
const CloseButton = forwardRef(({ props, ref }) => {
const {className, onClick, ...props} = props;
return (
<button className={`${className} close-button`}
onClick={(evt) => onClick(evt)}
tabIndex="0"
ref={ref}
{...props}
>Close</button>
);
});
При таком подходе у меня много ошибок: 1 - Cannot read properties of undefined (reading 'children') - Modal, 2 - ... className undefined in CloseButton etc. Ощущение что нельзя деструктурировать(непонятно почему). До форвард-реф хук и деструктуризации - все работало!
2 popup [наглядно показал его структуру], компоненты: Modal(переиспользую его в 1 popup) - InfoSuccess- CloseButton(переиспользую его в 1 popup) Во 2 у меня лишь 1 интерактивный элемент - button (tabindex) и все. Сейчас нет ни 1 идеи что делать с ним + ловушкой фокуса (focus-trap). :(
https://github.com/j3n4r3v/ligabank-credit/blob/master/src/components/success-modal/success-modal.jsx [SuccessModal full]
const SuccessModal = ({ className, onChangeVisibleSuccess }) => {
return (
<Modal onCloseModal={() => onChangeVisibleSuccess(false)}>
<InfoSuccess className={className} />
</Modal>
);
};
https://github.com/j3n4r3v/ligabank-credit/blob/master/src/components/info-block/info-block.jsx [Infoblock full]
const InfoBlock = ({ className, title, desc, type }) => {
return (
<section className={`info-block ${className} info-block--${type}`}>
<h3 className="info-block__title">{title}</h3>
<p className="info-block__desc">{desc}</p>
</section>
);
};
const InfoSuccess = ({ className }) => (
<InfoBlock
title="Спасибо за обращение в наш банк."
desc="Наш менеджер скоро свяжется с вами по указанному номеру телефона."
type="center"
className={className}
/>
);
Я знаю о подходе в моем случае: 3 компонента просто делаешь 1 = 1 компонент и никаких проблем с ловушкой фокуса (focus-trap). Но мне нужно переиспользовать компоненты чтобы не повторяться. Интересно понять что будет лучше в моем случае и почему (какие варианты), какая практика лучше - best practice. Прошу Вашей помощи, люди добрые ))