반응 후크를 사용하여 이벤트 핸들러가 잘못된 상태로 발사되는 이유는?
나는 이 회전하는 것의 복사본을 만들려고 한다.div
반응 후크 사용 예시.https://codesandbox.io/s/XDjY28XoV
지금까지 내 암호는 이렇다.
import React, { useState, useEffect, useCallback } from 'react';
const App = () => {
const [box, setBox] = useState(null);
const [isActive, setIsActive] = useState(false);
const [angle, setAngle] = useState(0);
const [startAngle, setStartAngle] = useState(0);
const [currentAngle, setCurrentAngle] = useState(0);
const [boxCenterPoint, setBoxCenterPoint] = useState({});
const setBoxCallback = useCallback(node => {
if (node !== null) {
setBox(node)
}
}, [])
// to avoid unwanted behaviour, deselect all text
const deselectAll = () => {
if (document.selection) {
document.selection.empty();
} else if (window.getSelection) {
window.getSelection().removeAllRanges();
}
}
// method to get the positionof the pointer event relative to the center of the box
const getPositionFromCenter = e => {
const fromBoxCenter = {
x: e.clientX - boxCenterPoint.x,
y: -(e.clientY - boxCenterPoint.y)
};
return fromBoxCenter;
}
const mouseDownHandler = e => {
e.stopPropagation();
const fromBoxCenter = getPositionFromCenter(e);
const newStartAngle =
90 - Math.atan2(fromBoxCenter.y, fromBoxCenter.x) * (180 / Math.PI);
setStartAngle(newStartAngle);
setIsActive(true);
}
const mouseUpHandler = e => {
deselectAll();
e.stopPropagation();
if (isActive) {
const newCurrentAngle = currentAngle + (angle - startAngle);
setIsActive(false);
setCurrentAngle(newCurrentAngle);
}
}
const mouseMoveHandler = e => {
if (isActive) {
const fromBoxCenter = getPositionFromCenter(e);
const newAngle =
90 - Math.atan2(fromBoxCenter.y, fromBoxCenter.x) * (180 / Math.PI);
box.style.transform =
"rotate(" +
(currentAngle + (newAngle - (startAngle ? startAngle : 0))) +
"deg)";
setAngle(newAngle)
}
}
useEffect(() => {
if (box) {
const boxPosition = box.getBoundingClientRect();
// get the current center point
const boxCenterX = boxPosition.left + boxPosition.width / 2;
const boxCenterY = boxPosition.top + boxPosition.height / 2;
// update the state
setBoxCenterPoint({ x: boxCenterX, y: boxCenterY });
}
// in case the event ends outside the box
window.onmouseup = mouseUpHandler;
window.onmousemove = mouseMoveHandler;
}, [ box ])
return (
<div className="box-container">
<div
className="box"
onMouseDown={mouseDownHandler}
onMouseUp={mouseUpHandler}
ref={setBoxCallback}
>
Rotate
</div>
</div>
);
}
export default App;
현재 mouseMoveHandler는 다음 상태로 호출됨isActive = false
비록 국가가 사실일지라도이 이벤트 핸들러를 올바른 상태로 작동시키려면 어떻게 해야 하는가?
또한 콘솔에 다음과 같은 경고가 기록되고 있다.
React Hook useEffect has missing dependencies: 'mouseMoveHandler' and 'mouseUpHandler'. Either include them or remove the dependency array react-hooks/exhaustive-deps
useEffect 종속성 어레이에 구성 요소 메서드를 포함해야 하는 이유나는 리액션 훅을 사용하는 다른 간단한 부품에 대해 이렇게 할 필요가 없었다.
감사합니다.
문제
왜 그럴까?
isActive
거짓인가?
const mouseMoveHandler = e => {
if(isActive) {
// ...
}
};
(편리상 참고사항 나는 단지 말하는 것이다.mouseMoveHandler
, 그러나 여기의 모든 것은 에 적용된다.mouseUpHandler
또한)
위의 코드가 실행되면 함수 인스턴스가 생성되어isActive
기능 폐쇄를 통해 가변적그 변수는 상수니까, 만약isActive
함수가 정의되면 항상 거짓이고false
해당 함수 인스턴스가 존재하는 한.
useEffect
또한 함수를 취하며, 그 함수는 당신의 함수에 대한 지속적인 참조를 가지고 있다.moveMouseHandler
함수 인스턴스 - useEffect 콜백(Effect Callback)이 존재하는 한 다음과 같은 복사본을 참조한다.moveMouseHandler
어디에isActive
거짓이다.
언제isActive
변경사항, 구성요소 렌더링 및 새로운 인스턴스moveMouseHandler
어떤 것이 만들어질 것인가.isActive
이다true
그러나useEffect
종속성이 변경된 경우에만 해당 기능을 다시 실행 - 이 경우 종속성(이 경우)[box]
)은 변하지 않았으므로,useEffect
실행되지 않고 의 버전moveMouseHandler
어디에isActive
false는 현재 상태와 관계없이 여전히 창문에 부착되어 있다.
이 때문에 '고갈'이 경고하는 것이다.useEffect
- 후크가 해당 종속성을 재실행하고 업데이트하지 않고 일부 종속성이 변경될 수 있음.
고치고 있다
훅이 간접적으로 의존하고 있기 때문에isActive
, 당신은 이것을 추가함으로써 고칠 수 있다.isActive
에게deps
을 위해 배열하다.useEffect
:
// Works, but not the best solution
useEffect(() => {
//...
}, [box, isActive])
하지만, 이것은 그다지 깨끗하지 않다: 만약 당신이 변화한다면.mouseMoveHandler
더 많은 상태에 따라 달라지기 위해서, 당신은 같은 버그를 갖게 될 것이다, 만약 당신이 와서 그것을 더하는 것을 기억하지 않는다면.deps
배열도. (또한 린터는 이것을 좋아하지 않을 것이다)
그useEffect
간접적으로 작용하다isActive
에 직접 달려있기 때문에mouseMoveHandler
; 대신 종속성에 추가할 수 있도록:
useEffect(() => {
//...
}, [box, mouseMoveHandler])
는 useEffect의 인 useEffect를 mouseMoveHandler
그 말은 존중한다는 뜻이지isActive
하지만 너무 자주 실행될 것이다. 매번 실행될 것이다.mouseMoveHandler
새로운 함수 인스턴스가 되다...새로운 기능이 모든 렌더에 생성되기 때문에 모든 렌더에 해당된다.
렌더링할 때마다 새로운 기능을 만들 필요는 없지만,isActive
변경됨:리액션은useCallback
그 사용 사례에 찬성하다.사용자 정의 가능mouseMoveHandler
로서
const mouseMoveHandler = useCallback(e => {
if(isActive) {
// ...
}
}, [isActive])
그리고 이제 새로운 함수 인스턴스는 단지isActive
그런 다음 트리거되는 변경 사항useEffect
적절한 순간에 실행할 수 있으며, 당신은 의 정의를 변경할 수 있다.mouseMoveHandler
(예: 상태 추가)를 중단하지 않고useEffect
낚싯바늘을 달다
이것은 여전히 당신의 문제를 야기시킬 수 있다.useEffect
매번 재방송할 거야 거야.isActive
변경 사항, 즉 매번 박스 중심점을 설정한다는 의미isActive
아마도 원치 않는 변화일 것이다.이 문제를 피하려면 효과를 두 개의 개별 효과로 나누십시오.
useEffect(() => {
// update box center
}, [box])
useEffect(() => {
// expose window methods
}, [mouseMoveHandler, mouseUpHandler]);
종료 결과
궁극적으로 코드는 다음과 같아야 한다.
const mouseMoveHandler = useCallback(e => {
/* ... */
}, [isActive]);
const mouseUpHandler = useCallback(e => {
/* ... */
}, [isActive]);
useEffect(() => {
/* update box center */
}, [box]);
useEffect(() => {
/* expose callback methods */
}, [mouseUpHandler, mouseMoveHandler])
추가 정보:
반응 작가 중 한 명인 Dan Abramov는 효과 블로그 포스트를 사용하기 위해 Complete Guide에 더 자세히 설명되어 있다.
반응 후크 useState+useEffect+이벤트는 오래된 상태를 제공한다.유사한 문제를 겪고 있는 것 같다. 기본적인 문제는 "그것이 정의된 폐쇄로부터 가치를 얻는 것"이다.
솔루션 2 "기준 사용"을 시도해 보십시오.당신의 시나리오로
아래에 useRef 및 useEffect 추가
let refIsActive = useRef(isActive);
useEffect(() => {
refIsActive.current = isActive;
});
그런 다음 마우스 안쪽에MoveHandler, 해당 참조를 사용하십시오.
const mouseMoveHandler = (e) => {
console.log('isActive',refIsActive.current);
if (refIsActive.current) {
나는 그것을 해결하기 위해 NPM 모듈을 만들었다.https://www.npmjs.com/package/react-usestateref
그리고 그것은 현재의 상태를 어떻게 발사할 것인가에 대한 당신의 질문에 대한 대답과 도움이 될 것 같다.
useState와 값을 수 있도록 .ref
사용 예:
const [isActive, setIsActive,isActiveRef] = useStateRef(false);
console.log(isActiveRef.current)
추가 정보:
'IT이야기' 카테고리의 다른 글
TS2339: 속성 'tsReducer'가 'DefaultRootState' 유형에 없음 (0) | 2022.03.24 |
---|---|
VueJ의 구성 요소 템플릿에서 Vuetify 대화 상자 열기s (0) | 2022.03.24 |
기본 반응: Reducx-perist, 지속되지 않는 작업의 결과 (0) | 2022.03.24 |
파이톤 대본에 #!(쉐뱅)을 넣어야 하나, 어떤 형식을 취해야 하나? (0) | 2022.03.24 |
Vuejs : 오류:현재 위치로 중복 탐색 방지: (0) | 2022.03.24 |