sourcetip

React Functional Components의 모든 함수 핸들러에서 Callback을 사용해야 합니까?

fileupload 2023. 3. 9. 22:17
반응형

React Functional Components의 모든 함수 핸들러에서 Callback을 사용해야 합니까?

예를 들어 다음과 같은 컴포넌트가 있다고 합시다.

const Example = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = () => setCounter(counter => counter + 1); 
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

지나갔을 때onClick화살표 기능으로 핸들러, myeslint경고:

error    JSX props should not use arrow functions        react/jsx-no-bind

이 투고로부터의 회답으로부터 읽었습니다.https://stackoverflow.com/questions/36677733/why-shouldnt-jsx-props-use-arrow-functions-or-bind # : ~ : text = Why % 20 your % 20 do not use , previous % 20 function % 20 is % 20 garbage % 20 collected .

단답은 매번 화살표 기능이 재현돼 성능이 떨어지기 때문이다.이 투고에서 제안하는 솔루션 중 하나는 빈 어레이로 useCallback 후크에 랩하는 것입니다.그리고 이걸 바꾸면 eslint 경고는 정말 사라집니다.

const Example = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = useCallback(() => setCounter(counter => counter + 1), []);
  
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

그러나 useCallback을 과도하게 사용하면 useCallback의 오버헤드로 인해 결과적으로 퍼포먼스가 저하된다는 의견도 있습니다.예를 들면, https://kentcdodds.com/blog/usememo-and-usecallback 입니다.

이게 날 혼란스럽게 한다고?기능 컴포넌트의 경우 인라인 함수 핸들러를 다룰 때 화살표 함수를 쓸까요(eslint를 무시함) 아니면 항상 useCallback으로 래핑해야 합니까?

단답은 매번 화살표 기능이 재현돼 성능이 떨어지기 때문이다.

이것은 일반적인 오해입니다.화살표 기능은 어느 쪽이든 매번 재생성됩니다(단,useCallback후속 제품은 즉시 폐기할 수 있습니다.)뭐?useCallback는 콜백을 사용하는 자 컴포넌트가 메모되어 있는 경우 다시 콜백되지 않도록 합니다.

그럼 오해부터 살펴볼까요?고려 사항:useCallback호출:

const increment = useCallback(() => setCounter(counter => counter + 1), []);

다음과 같이 실행됩니다.

  1. 첫 번째 인수를 평가합니다.() => setCounter(counter => counter + 1), 함수를 만듭니다.

  2. 두 번째 인수를 평가합니다.[], 어레이 작성

  3. 불러useCallback이 두 개의 인수로 함수를 되돌립니다.

사용하지 않을 경우 가지고 있는 것과 비교해 보십시오.useCallback:

const increment = () => setCounter(counter => counter + 1);

훨씬 간단합니다. 함수를 만듭니다.그러면 위의 2번과 3번을 할 필요가 없습니다.

그럼 뭐로 넘어갑시다useCallback그게 실제로 도움이 되거든요.콜백의 사용처를 살펴보겠습니다.

<Button onClick={increment} />

자, 예를 들어Button또는 이와 유사합니다.한다면increment컴포넌트가 렌더링될 때마다 변경됩니다.Button는 컴포넌트가 변경될 때마다 다시 작성해야 합니다.렌더 간에 재사용할 수 없습니다., 만약 ★★★★★★★★★★★★★★★★.increment에) 입니다.useCallback을 사용)를 호출했을 된 결과입니다.Button다시 호출할 필요가 없기 때문에 다시 사용할 수 있습니다.

다음은 예를 제시하겠습니다.

const { useState, useCallback } = React;

const Button = React.memo(function Button({onClick, children}) {
    console.log("Button called");
    return <button onClick={onClick}>{children}</button>;
});

function ComponentA() {
    console.log("ComponentA called");
    const [count, setCount] = useState(0);
    // Note: Safe to use the closed-over `count` here if `count `updates are
    // triggered by clicks or similar events that definitely render, since
    // the `count` that `increment` closes over won't be stale.
    const increment = () => setCount(count + 1);
    return (
        <div>
            {count}
            <Button onClick={increment}>+</Button>
        </div>
    );
}

function ComponentB() {
    console.log("ComponentB called");
    const [count, setCount] = useState(0);
    // Note: Can't use `count` in `increment`, need the callback form because
    // the `count` the first `increment` closes over *will* be slate after
    // the next render
    const increment = useCallback(
        () => setCount(count => count + 1),
        []
    );
    return (
        <div>
            {count}
            <Button onClick={increment}>+</Button>
        </div>
    );
}

ReactDOM.render(
    <div>
        A:
        <ComponentA />
        B:
        <ComponentB />
    </div>,
    document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

해 주세요.ComponentA 전화하다Button한 번 ''를 클릭해 주세요.ComponentB그렇지 않다.

신은언 언것 ?것? ??? ???주로 컴포넌트의 가 자주 은 아마 것입니다. 내용은 을 받지 increment을 주지 Button 그리고 만약Button렌더링할 때 중요한 작업을 수행해야 합니다. Button그렇지 않을 수도 있지만 다른 아이 구성 요소도 그럴 수 있습니다.

를 들면, 「」는,useCallback에서는, 「아까운 사람」이라고 하는 것은 할 것입니다.count은 「일부러」를 의미하기 에, 「일부러」를 합니다.Button 다음의 를 불문하고 재접속할 가 있습니다.

const { useState, useCallback } = React;

const Button = React.memo(function Button({onClick, children}) {
    console.log("Button called");
    return <button onClick={onClick}>{children}</button>;
});

function ComponentA() {
    console.log("ComponentA called");
    const [count, setCount] = useState(0);
    // Note: Safe to use the closed-over `count` here if `count `updates are
    // triggered by clicks or similar events that definitely render, since
    // the `count` that `increment` closes over won't be stale.
    const increment = () => setCount(count + 1);
    return (
        <div>
            <Button onClick={increment}>{count}</Button>
        </div>
    );
}

function ComponentB() {
    console.log("ComponentB called");
    const [count, setCount] = useState(0);
    // Note: Can't use `count` in `increment`, need the callback form because
    // the `count` the first `increment` closes over *will* be slate after
    // the next render
    const increment = useCallback(
        () => setCount(count => count + 1),
        []
    );
    return (
        <div>
            <Button onClick={increment}>{count}</Button>
        </div>
    );
}

ReactDOM.render(
    <div>
        A:
        <ComponentA />
        B:
        <ComponentB />
    </div>,
    document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

, 「 」, 「 」라고 하는 도 주의해 주세요.useCallback무료가 아니라 콜백의 코드에 영향을 줍니다.에 있는 .ComponentA ★★★★★★★★★★★★★★★★★」ComponentB를 참조해 주세요. ComponentA하지 않음)useCallback의할 수 .count.() => setCount(count + 1) 그 에 있는 건ComponentB인 '콜백'을 .() => setCount(count => count + 1)increment 「」가 됩니다.count종료는 오래된 것입니다.카운트는 1이 됩니다만, 그 이상은 아닙니다.


여러가지 만들고 경우useCallback ★★★★★★★★★★★★★★★★★」useMemo메모리 이탈이 너무 심할 수 있습니다(매우 드문 경우).ref를 사용하면 이를 방지할 수 있습니다.업데이트에 대해 알아보겠습니다.ComponentB대신 ref를 사용한다.useCallback:

const incrementRef = useRef(null);
if (!incrementRef.current /* || yourDependenciesForItChange*/) {
    // Note: Can't use `count` in `increment`, need the callback form because
    // the `count` the first `increment` closes over *will* be slate after
    // the next render
    incrementRef.current = () => setCount(count => count + 1);
}
const increment = incrementRef.current;

" " "만됩니다.increment(예에서는에) 번 (사용할 수 있는 기능이 없습니다)를 사용하는 일은 없습니다.useCallback이 'ref'이기 가 있습니다.null 후 가 처음 되었을 때, 함수가 「」라고 null함수를 생성하여 참조에 추가합니다. ★★★★★★★★★★★★★★★★★.increment한 번만 생성됩니다.

에서는 우리가 .setCountincrement피할, 피할 수 피할있다.

const incrementRef = useRef(null);
if (!incrementRef.current) {
    // Note: Can't use `count` in `increment`, need the callback form because
    // the `count` the first `increment` closes over *will* be slate after
    // the next render
    const incrementCallback = count => count + 1;
    incrementRef.current = () => setCount(incrementCallback);
}
const increment = incrementRef.current;

const { useState, useRef } = React;

const Button = React.memo(function Button({onClick, children}) {
    console.log("Button called");
    return <button onClick={onClick}>{children}</button>;
});

function ComponentA() {
    console.log("ComponentA called");
    const [count, setCount] = useState(0);
    // Note: Safe to use the closed-over `count` here if `count `updates are
    // triggered by clicks or similar events that definitely render, since
    // the `count` that `increment` closes over won't be stale.
    const increment = () => setCount(count + 1);
    return (
        <div>
            {count}
            <Button onClick={increment}>+</Button>
        </div>
    );
}

function ComponentB() {
    console.log("ComponentB called");
    const [count, setCount] = useState(0);
    const incrementRef = useRef(null);
    if (!incrementRef.current) {
        // Note: Can't use `count` in `increment`, need the callback form because
        // the `count` the first `increment` closes over *will* be slate after
        // the next render
        const incrementCallback = count => count + 1;
        incrementRef.current = () => setCount(incrementCallback);
    }
    const increment = incrementRef.current;
    return (
        <div>
            {count}
            <Button onClick={increment}>+</Button>
        </div>
    );
}

ReactDOM.render(
    <div>
        A:
        <ComponentA />
        B:
        <ComponentB />
    </div>,
    document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

불필요한 함수 생성을 회피한다는 점에서 이는 11이 됩니다. :-)

이 컴포넌트는 제1레벨의 최적화를 필요로 하는 드문 컴포넌트입니다.제2레벨의 최적화를 필요로 하는 경우는 거의 없습니다.하지만 최적화가 필요한 경우에는 그렇게 해야 합니다.

언급URL : https://stackoverflow.com/questions/64134566/should-we-use-usecallback-in-every-function-handler-in-react-functional-componen

반응형