사용자가 검색 창에 입력 할 때 "자동 완성"권장 사항 목록을 표시하는 React 구성 요소가 있습니다. 사용자가 화살표 키와 Enter 키를 사용하여 목록을 탐색 할 수 있도록하려고합니다.
사용자가 처음으로 아래쪽 키를 누르면 포커스 변수가 1 씩 증가하고 HTML 포커스가 첫 번째 권장 사항으로 이동합니다. 다시 누르면 포커스 변수가 다시 1 씩 증가하고 HTML 포커스가 두 번째 권장 사항으로 이동하는 식입니다. 마찬가지로 사용자가 위쪽 키를 누르면 포커스가 1 씩 줄어들고 HTML 포커스가 이전 권장 사항으로 이동합니다. 또한 포커스 인덱스가 가능한 범위를 벗어나지 않도록하는 조치와 검색 바의 값이 변경 될 때 포커스 인덱스를 0으로 재설정하는 조치가 있습니다.
포커스 인덱스 값을 재정의 할 때 아래쪽 키를 사용할 때만 제대로 작동하고 위쪽 키를 누르면 재정의 이전의 이전 값 형식으로 변경된다는 이상한 오류가 발생합니다. 더 긴 검색을 입력하면 권장 사항 목록이 좁아지기 때문에 어떤 경우에는 포커스 인덱스가 범위를 벗어난 다음 충돌이 발생할 수 있습니다.
다음은 하나의 문자 만 검색 할 때 포커스 인덱스에 대한 콘솔 출력의 일부 스크린 샷입니다.
이제 검색 범위를 좁힌 다음 권장 사항을 탐색 할 때 어떤 일이 발생하는지 보여 드리겠습니다.
다음은 작동하는 코드 및 상자 링크입니다.
https://codesandbox.io/s/jolly-tu-7itsm?fontsize=14&hidenavigation=1&theme=dark
좋아, 내가 예상했던 것과 똑같다. 당신은 동봉 한 일부 focus
독립적으로의 변이 된 "의 keyup"이벤트 핸들러에서 값 focus
존재 각 주요 구성 요소 본문에주기를 렌더링 재설정 useEffect
할 때 종속성 업데이트 후크를.
이 효과에 대한 코드 및 상자에 반응 린트 경고도 있습니다.
React Hook useEffect 내부에서 'focus'변수에 대한 할당은 각 렌더링 후 손실됩니다. 시간이 지남에 따라 값을 보존하려면 useRef 후크에 저장하고 '.current'속성에 변경 가능한 값을 유지하십시오.
useEffect
후크를 사용하여 "keyup"이벤트 리스너를 관리하고 구성 요소가 마운트 해제 될 때이를 제거하는 정리 기능을 사용하는 것이 좋습니다 . 이렇게하면 효과 콜백이 트리거 될 때 마다 새 리스너가 추가되는 것을 방지 할 수 있습니다 ( 코드가 렌더링 할 때마다 리스너를 추가했습니다 !! )."autocomplete__container"
직접 DOM 쿼리 및 조작이 React에서 안티 패턴으로 간주 되므로 컨테이너 를 참조하기 위해 React ref를 사용하는 것이 좋습니다 .암호:
function App() {
const [value, setValue] = useState("");
const [items, setItems] = useState([]);
const [query, setQuery] = useState([]);
const [formSubmit, setFormSubmit] = useState(false);
const [completions] = useAutocomplete(value, autocompleteValues);
const autocompleteContainer = useRef(); // <-- ref to get DOMNode
const focusRef = useRef(-1); // <-- ref to store stable focus value
...
// Effect hook to manage keyup event handler and cleanup
useEffect(() => {
const handleKeypress = (event) => {
if (event.keyCode === 38 && focusRef.current >= 1) { // <-- focus.current
focusRef.current -= 1;
autocompleteContainer.current.childNodes[focusRef.current].focus(); // <-- autocompleteContainer.current
} else if (
event.keyCode === 40 &&
focusRef.current < autocompleteContainer.current.childElementCount - 1
) {
focusRef.current += 1;
autocompleteContainer.current.childNodes[focusRef.current].focus();
} else if (
event.keyCode === 13 &&
document.activeElement.className !== "form-control"
) {
setValue(document.activeElement.innerHTML);
setQuery(document.activeElement.innerHTML);
document.getElementsByClassName(
"autocomplete__container"
)[0].style.display = "none";
setFormSubmit(true);
}
};
window.addEventListener("keyup", handleKeypress); // <-- add listener
return () => window.removeEventListener("keyup", handleKeypress); // <-- return cleanup function to remove listener
}, []);
// Effect hook to reset focus value when list updates
useEffect(() => {
focusRef.current = -1;
console.log("focus reset", focusRef.current);
}, [value, items]);
return (
<div className="App">
<Form id="search__form" onSubmit={handleSubmit}>
<Form.Group>
<InputGroup className="mb-3">
...
<div
ref={autocompleteContainer} // <-- attach DOM ref here
className="autocomplete__container"
>
{completions.map((val, index) => (
<p
tabIndex={index + 10}
key={index}
onClick={() => updateInput({ val })}
>
{val}
</p>
))}
</div>
</InputGroup>
</Form.Group>
</Form>
<p>{focusRef.current}</p>
</div>
);
}
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다