React Hooks에서 함수 재작성 억제 useEventCallback
19327 단어 Reactreact-hooks
useCallback
를 사용하여 함수를 기록하고 재사용할 수 있습니다.이렇게하면 memo 화 된 하위 구성 요소에서 props의 불필요한 업데이트로 인한 re-render를 피할 수 있습니다.
useCallback 사용 전
// Buttonコンポーネントはpropsがshallow equalであればrenderされない
const Button = React.memo(props => {
const { label, onClick } = props;
console.log(`Button "${label}" is rendered`);
return <button onClick={onClick}>{label}</button>;
});
//
export const CounterComp = props => {
const [count, setCount] = useState(0);
// onIncrement/onDecrement は render実行ごとに毎回異なるものが生成される
const onIncrement = () => setCount(count + 1);
const onDecrement = () => setCount(count - 1);
// ゆえに下の`Button`は毎回renderされる
return (
<>
<div>Count: {count}</div>
<Button label="+" onClick={onIncrement} />
<Button label="-" onClick={onDecrement} />
</>
);
};
useCallback 사용 후
// Buttonコンポーネントはpropsがshallow equalであればrenderされない
const Button = React.memo(props => {
const { label, onClick } = props;
console.log(`Button "${label}" is rendered`);
return <button onClick={onClick}>{label}</button>;
});
//
export const CounterComp = props => {
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
// onIncCountA/onIncCountB は再利用される
const onIncCountA = useCallback(() => setCountA(countA + 1), [countA]);
const onIncCountB = useCallback(() => setCountB(countB + 1), [countB]);
// 片方のcounterが更新されても、もう一方の`Button`のrenderは避けられる
return (
<>
<div>
A = {countA}, B = {countB}
</div>
<Button label="A++" onClick={onIncCountA} />
<Button label="B++" onClick={onIncCountB} />
</>
);
};
그러나,
useCallback
를 이용하고 있어도 의존하는 변수가 업데이트 되고 있으면 역시 콜백은 새롭게 재작성되어 버려, Button 컴퍼넌트에 있어서 불필요한 render 가 발생해 버립니다.이를 피하기 위해
useEventCallback
라는 사용자 정의 후크를 만드는 방법이 있습니다.htps : // 기주 b. 코 m / 후세 보오 k / 레아 ct / 이스에 s / 14099 # 이스에 코멘 t-440013892
htps : // Rea ctjs. rg/do cs/호오 ks-후 q. html # HO W-T-REA-D-AN-O-FEN-changin-g
위의 두 가지는 각각 구현이 약간 다릅니다만, 하려고 하는 것은 대체로 같습니다.
TypeScript로 형식을 의식하고 쓰면 이런 느낌입니까?
useEventCallback.ts
import { useRef, useCallback, useLayoutEffect } from 'react';
export function useEventCallback<A extends any[], R>(
callback: (...args: A) => R,
): (...args: A) => R {
const callbackRef = useRef<typeof callback>(() => {
throw new Error('Cannot call an event handler while rendering.');
});
useLayoutEffect(() => {
callbackRef.current = callback;
}, [callback]);
return useCallback(
(...args: A) => {
const callback = callbackRef.current;
return callback(...args);
},
[],
);
}
이것 (
useEventCallback
)을 사용하면 앞의 예는 다음과 같습니다.useEventCallback 사용
// カスタムHook
function useEventCallback(callback) {
const callbackRef = useRef();
useLayoutEffect(() => {
callbackRef.current = callback;
}, [callback]);
return useCallback((...args) => {
const callback = callbackRef.current;
return callback(...args);
}, []);
}
// Buttonコンポーネントはpropsがshallow equalであればrenderされない
const Button = React.memo(props => {
const { label, onClick } = props;
console.log(`Button "${label}" is rendered`);
return <button onClick={onClick}>{label}</button>;
});
//
export const CounterComp = props => {
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
// onIncCountA/onIncCountB は常に同じ関数が再利用される
const onIncCountA = useEventCallback(() => setCountA(countA + 1));
const onIncCountB = useEventCallback(() => setCountB(countB + 1));
// `Button`のre-renderは常に避けられる
return (
<>
<div>
A = {countA}, B = {countB}
</div>
<Button label="A++" onClick={onIncCountA} />
<Button label="B++" onClick={onIncCountB} />
</>
);
};
매우 간단하고 잘 보입니다.
다만, 이 방법은 상기의 Hooks FAQ 에서는 그다지 추천하지 않는다, 라고 하는 느낌이군요.
Note
We recommend to pass dispatch down in context rather than individual callbacks in props. The approach below is only mentioned here for completeness and as an escape hatch.
Also note that this pattern might cause problems in the concurrent mode. We plan to provide more ergonomic alternatives in the future, but the safest solution right now is to always invalidate the callback if some value it depends on changes.
흠, Concurrent Mode 로 문제가 나올지도 모릅니다.
다만, render 페이즈로 callback ref를 mutate 하는 것이 아니라
useLayoutEffect
로 하면 그 별 문제 없을 것 같아도 생각됩니다만, 우선 케이스가 나온다(혹은 향후 나오지 않을 것을 보증할 수 없다) 그렇죠?
Reference
이 문제에 관하여(React Hooks에서 함수 재작성 억제 useEventCallback), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/stomita/items/4bb5068c219ca16d9823텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)