我正在使用 React 开发一个非常基本的笔记应用程序。纯粹是前端问题。
在侧边栏上,我有一个所有当前笔记的列表,用户可以使用它们在它们之间进行交换。列表中显示的每个注释的右侧都有一个删除按钮,可以删除该注释。单击该按钮后,将出现一个 div(我称之为“批准框”),要求确认。 “删除?是还是不是。”批准框基于布尔值 useState 显示。
这就是我正在尝试做的事情。如果用户单击页面上的其他任何位置,我希望取消批准框。我的目的是将焦点设置在 div 上,然后使用 onBlur 事件切换 useState,然后关闭 div。如果焦点丢失,则不再有批准框。
问题是我一生都无法弄清楚如何真正将焦点集中在批准框上。
这是我正在编写的组件的代码:
import { useRef } from 'react';
import { useAppContext } from '../../App';
import { BsX } from 'react-icons/bs';
const useFocus = () => {
const htmlElRef = useRef(null);
const setFocus = () => {
htmlElRef.current && htmlElRef.current.focus();
};
return [htmlElRef, setFocus];
};
const DeleteButton = ({ note, showApproval, setShowApproval }) => {
const { deleteSelectedNote } = useAppContext();
const [approvalBoxRef, setApprovalBoxFocus] = useFocus();
const handleClickDelete = () => {
setShowApproval(!showApproval);
setApprovalBoxFocus();
};
const handleConfirmDelete = (noteId) => {
deleteSelectedNote(noteId);
};
return (
<div className='delete-btn-container'>
<button
className={showApproval ? 'delete-btn btn pending' : 'delete-btn btn'}
onClick={handleClickDelete}
>
<BsX size={'1em'} />
</button>
<div
className={showApproval ? 'approval-box active' : 'approval-box'}
tabIndex='1'
onBlur={() => {
setShowApproval(false);
console.log('approval removed');
}}
ref={approvalBoxRef}
>
Delete?
<span className='yes' onClick={() => handleConfirmDelete(note.id)}>
{' '}
Y{' '}
</span>
/
<span className='no' onClick={() => setShowApproval(false)}>
{' '}
N
</span>
</div>
</div>
);
};
export default DeleteButton;
现在,我在批准框中将 tabIndex 设置为 1,并且当用户单击初始删除按钮时,我使用自定义挂钩将焦点设置在其上。
但是当我这样做时,批准框没有获得焦点。用户仍然可以单击并与应用程序的其余部分交互,并且批准框的 onBlur 事件永远不会触发。仅当用户实际单击审批框时,焦点才会设置在审批框上。然后,如果他们点击离开,就会发生所需的效果,并且批准框将被关闭。
关于焦点如何运作,有什么我不明白的地方吗?
我不知道这是否重要,但正如您在代码中看到的,这是我的列表中单个注释项的删除按钮。每个笔记都会有其中之一,我希望用户一次只能与其中一个进行交互。
审批框始终呈现,但我使用 CSS 使其可见且具有交互性。我尝试过有条件地渲染批准框而不是仅仅显示它,但在这种情况下焦点问题是相同的。
如果有更好的方法来达到我想要的效果,我绝对愿意接受有关替代方案的建议。
您可以创建一个
useClickOutside.js
钩子
import { useEffect, useCallback } from 'react';
const isRefArray = r => 'length' in r;
const isTarget = (ref, event) =>
ref && ref.current && ref.current.contains(event.target);
const trueForAny = (array, condition) =>
array.reduce(
(conditionAlreadyMet, value) => conditionAlreadyMet || condition(value),
false,
);
const useClickOutside = (ref, onclick) => {
const handleClick = useCallback(
click => {
if (isRefArray(ref)) {
if (trueForAny(ref, d => isTarget(d, click))) {
return;
}
} else if (isTarget(ref, click)) {
return;
}
onclick();
},
[onclick, ref],
);
useEffect(() => {
document.addEventListener('click', handleClick);
return () => {
document.removeEventListener('click', handleClick);
};
}, [handleClick]);
return ref;
};
export default useClickOutside;
上面的钩子的作用是监视您何时在传递的
ref
组件之外单击。
在您的组件文件中,执行此操作
import { useState, useRef } from 'react';
import useClickOutside from 'hooks/useClickOutside';
const clickRef = useRef();
useClickOutside(clickRef, () => {
// Set the state to close the approval-box on click outside here
setShowApproval(false);
});
function DeleteButton(){
...
return(
<div className='delete-btn-container'>
<button
ref={clickRef)
className={showApproval ? 'delete-btn btn pending' : 'delete-btn btn'}
onClick={handleClickDelete}
>
<BsX size={'1em'} />
</button>
...
</div>
)
}
您可以摆脱
useFocus
函数和 focus
实现。