이벤트 리스너에서의 잘못된 리액트 후크 동작
리액트 훅을 가지고 놀다가 문제가 생겼어요.이벤트 리스너에 의해 처리되는 버튼을 사용하여 콘솔로그를 실행하려고 하면 잘못된 상태가 표시됩니다.
Code Sandbox: https://codesandbox.io/s/lrxw1wr97m
- 'Add card' 버튼을 두 번 클릭합니다.
- 첫 번째 카드에서 버튼 1을 클릭하여 콘솔에서 카드가 2개 있는지 확인합니다(올바른 동작).
- 첫 번째 카드에서 Button 2(이벤트 리스너에 의해 처리됨)를 클릭하여 콘솔에 카드가1개밖에 없는 것을 확인합니다(잘못된 동작).
왜잘 상태 태태 태? ???
번째 카드에는 '''가 있습니다.Button2
를 표시해야 합니다.2
카드를 사용할 수 있습니다.은은생 각각?
const { useState, useContext, useRef, useEffect } = React;
const CardsContext = React.createContext();
const CardsProvider = props => {
const [cards, setCards] = useState([]);
const addCard = () => {
const id = cards.length;
setCards([...cards, { id: id, json: {} }]);
};
const handleCardClick = id => console.log(cards);
const handleButtonClick = id => console.log(cards);
return (
<CardsContext.Provider
value={{ cards, addCard, handleCardClick, handleButtonClick }}
>
{props.children}
</CardsContext.Provider>
);
};
function App() {
const { cards, addCard, handleCardClick, handleButtonClick } = useContext(
CardsContext
);
return (
<div className="App">
<button onClick={addCard}>Add card</button>
{cards.map((card, index) => (
<Card
key={card.id}
id={card.id}
handleCardClick={() => handleCardClick(card.id)}
handleButtonClick={() => handleButtonClick(card.id)}
/>
))}
</div>
);
}
function Card(props) {
const ref = useRef();
useEffect(() => {
ref.current.addEventListener("click", props.handleCardClick);
return () => {
ref.current.removeEventListener("click", props.handleCardClick);
};
}, []);
return (
<div className="card">
Card {props.id}
<div>
<button onClick={props.handleButtonClick}>Button1</button>
<button ref={node => (ref.current = node)}>Button2</button>
</div>
</div>
);
}
ReactDOM.render(
<CardsProvider>
<App />
</CardsProvider>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id='root'></div>
React 16.7.0-alpha.0과 Chrome 70.0.3538.110을 사용하고 있습니다.
참고로, CardsProvider를 "lass"를 사용하여 다시 쓰면 문제가 해소됩니다.클래스를 사용하는 CodeSandbox: https://codesandbox.io/s/w2nn3mq9vl
은 기능적인 했을 때 볼 수 있는 입니다.useState
됩니다.useState
상태가 사용됩니다(예: 또는 타이머 기능).
는 에서는 다르게 됩니다.CardsProvider
★★★★★★★★★★★★★★★★★」Card
구성 요소들.
handleCardClick
★★★★★★★★★★★★★★★★★」handleButtonClick
used the the CardsProvider
기능 컴포넌트가 그 범위에 정의되어 있습니다.이 실행될 기능이 이 은 이 이 작동하다를 말합니다.이러한 기능은cards
정의된 시점에 취득된 상태.는, 핸들러, 핸들러가 됩니다.CardsProvider
이치노
handleCardClick
used the the Card
에 한 번 합니다.useEffect
한 기능을 이 은 컴포넌트 수명 전체 동안 handleCardClick
이치노 handleButtonClick
각 됩니다.Card
렌더링, 매번 새로운 기능으로 새로운 상태를 나타냅니다.
가변 상태
하기 위한 은 " " " 입니다.useRef
useState
. 될 수 있는 ref는 기본적으로 참조에 의해 전달될 수 있는 가변 객체를 제공하는 레시피입니다.
const ref = useRef(0);
function eventListener() {
ref.current++;
}
이 시 합니다.이 경우 컴포넌트는 스테이트업데이트 시 .useState
참조는 적용되지 않습니다.
한 상태를 할 수 " " " " " " " " " " " " "forceUpdate
는 클래스 컴포넌트와 함수컴포넌트 모두에서 안티 패턴으로 간주됩니다(참고용으로만 리스트 되어 있습니다).
const useForceUpdate = () => {
const [, setState] = useState();
return () => setState({});
}
const ref = useRef(0);
const forceUpdate = useForceUpdate();
function eventListener() {
ref.current++;
forceUpdate();
}
상태 업데이트 프로그램 기능
한 가지 해결책은 동봉된 범위에서 오래된 상태가 아닌 새로운 상태를 수신하는 상태 업데이터 기능을 사용하는 것입니다.
function eventListener() {
// doesn't matter how often the listener is registered
setState(freshState => freshState + 1);
}
, 으로 상태가 합니다.console.log
회피책은 업데이트를 방지하기 위해 동일한 상태를 반환하는 것입니다.
function eventListener() {
setState(freshState => {
console.log(freshState);
return freshState;
});
}
useEffect(() => {
// register eventListener once
return () => {
// unregister eventListener once
};
}, []);
비동기 부작용에는 잘 .async
★★★★★★★★★★★★★★★★★★.
수동 이벤트 리스너 재등록
또 하나의 해결책은 이벤트청취자를 매번 재등록하여 콜백이 항상 동봉된 스코프에서 새로운 상태를 얻을 수 있도록 하는 것입니다.
function eventListener() {
console.log(state);
}
useEffect(() => {
// register eventListener on each state update
return () => {
// unregister eventListener
};
}, [state]);
내장 이벤트 처리
가 에 되어 있지 않은 document
,window
컴포넌트의 Resact DOM 한 해야 합니다.에 의해, 「Resact」는.이것에 의해, 다음의 조작이 불필요하게 됩니다.useEffect
:
<button onClick={eventListener} />
할 수 .useMemo
★★★★★★★★★★★★★★★★★」useCallback
소품으로 전달될 때 불필요한 재접촉을 방지하기 위해:
const eventListener = useCallback(() => {
console.log(state);
}, [state]);
- 에서는 초기 판에 수 가변 했습니다.
useState
React 16.7.0-alpha 버전에서는 훅 구현이 가능하지만 최종 React 16.8 구현에서는 사용할 수 없습니다.useState
현재 지원되는 것은 불변 스테이트뿐입니다.*
은 내가 '라고 부르는 후크를 것입니다.useStateRef
function useStateRef(initialValue) {
const [value, setValue] = useState(initialValue);
const ref = useRef(value);
useEffect(() => {
ref.current = value;
}, [value]);
return [value, setValue, ref];
}
해서 '어울리다'를 할 수 되었습니다.ref
이치
즉, useState에는 다음과 같은 간단한 솔루션이 있습니다.
function Example() {
const [state, setState] = useState(initialState);
function update(updates) {
// this might be stale
setState({...state, ...updates});
// but you can pass setState a function instead
setState(currentState => ({...currentState, ...updates}));
}
//...
}
간단한 답변입니다.
myvar가 변경될 때마다 재렌더가 트리거되지 않습니다.
const [myvar, setMyvar] = useState('')
useEffect(() => {
setMyvar('foo')
}, []);
이 WILL은 렌더 -> myvar를 []에 넣습니다.
const [myvar, setMyvar] = useState('')
useEffect(() => {
setMyvar('foo')
}, [myvar]);
콘솔을 확인하면 다음과 같은 답이 나옵니다.
React Hook useEffect has a missing dependency: 'props.handleCardClick'. Either include it or remove the dependency array. (react-hooks/exhaustive-deps)
더하면 돼요.props.handleCardClick
올바르게 동작합니다.
이렇게 하면 콜백의 상태 값이 항상 갱신됩니다.
// registers an event listener to component parent
React.useEffect(() => {
const parentNode = elementRef.current.parentNode
parentNode.addEventListener('mouseleave', handleAutoClose)
return () => {
parentNode.removeEventListener('mouseleave', handleAutoClose)
}
}, [handleAutoClose])
Moses Gitau의 훌륭한 답변을 바탕으로 Typescript를 개발하고 있다면, Type 오류를 해결하기 위해 후크 함수를 일반 함수로 만듭니다.
function useStateRef<T>(initialValue: T | (() => T)):
[T, React.Dispatch<React.SetStateAction<T>>, React.MutableRefObject<T>] {
const [value, setValue] = React.useState(initialValue);
const ref = React.useRef(value);
React.useEffect(() => {
ref.current = value;
}, [value]);
return [value, setValue, ref];
}
@Moses Gitau의 답변부터 시작해서, 저는 가치의 "지연" 버전에 접근할 수 없고, 조금 더 미니멀리즘적인 것을 사용하고 있습니다.
import { useState, useRef } from 'react';
function useStateRef(initialValue) {
const [, setValueState] = useState(initialValue);
const ref = useRef(initialValue);
const setValue = (val) => {
ref.current = val;
setValueState(val); // to trigger the refresh
};
const getValue = (val) => {
return ref.current;
};
return [getValue , setValue];
}
export default useStateRef;
이게 내가 쓰고 있는 거야
사용 예:
const [getValue , setValue] = useStateRef(0);
const listener = (event) => {
setValue(getValue() + 1);
};
useEffect(() => {
window.addEventListener('keyup', listener);
return () => {
window.removeEventListener('keyup', listener);
};
}, []);
Edit : 참조 자체가 아닌 getValue가 표시됩니다.그럴 때는 좀 더 캡슐화해 두는 게 좋을 것 같아요.
후index.js
을 제기하다button2
정상적으로 동작합니다.
useEffect(() => {
ref.current.addEventListener("click", props.handleCardClick);
return () => {
ref.current.removeEventListener("click", props.handleCardClick);
};
- }, []);
+ });
하면 안 요.[]
인수로서useEffect
한번면면면면면면면면면면면면
상세: https://reactjs.org/docs/hooks-effect.html
언급URL : https://stackoverflow.com/questions/53845595/wrong-react-hooks-behaviour-with-event-listener
'programing' 카테고리의 다른 글
WordPress rest API OAuth curl 명령 (0) | 2023.03.07 |
---|---|
.NET 4에는 JSON 시리얼라이저/디시리얼라이저가 내장되어 있습니까? (0) | 2023.03.07 |
중복된 결과를 ng-repeat 필터에서 제외하는 방법 (0) | 2023.03.07 |
SonarQube 규칙: Spring Boot 응용 프로그램에서 "명령줄 인수를 사용하는 것은 보안에 영향을 미칩니다" (0) | 2023.03.02 |
출력 텍스트 파일에서 열 머리글 제거 (0) | 2023.03.02 |