programing

대응: useState 또는 useRef?

sourcetip 2023. 4. 2. 20:58
반응형

대응: useState 또는 useRef?

는 리액트 기사를 요.useState() ★★★★★★★★★★★★★★★★★」useRef()"Hooks FAQ"에서 useRef와 useState를 동시에 사용하는 솔루션이 있는 것 같은 몇 가지 사용 사례에 대해 혼란스러웠고 어느 쪽이 올바른 방법인지 잘 모르겠습니다.

useRef()에 대한 "Hooks FAQ"에서 다음을 수행합니다.

useRef() Hook은 DOM ref만을 위한 것이 아닙니다."ref" 개체는 현재 속성이 변경 가능하고 클래스의 인스턴스 속성과 유사한 모든 값을 포함할 수 있는 일반 컨테이너입니다.

useRef()의 경우:

function Timer() {
  const intervalRef = useRef();

  useEffect(() => {
    const id = setInterval(() => {
      // ...
    });
    intervalRef.current = id;
    return () => {
      clearInterval(intervalRef.current);
    };
  });

  // ...
}

useState()의 경우:

function Timer() {
  const [intervalId, setIntervalId] = useState(null);

  useEffect(() => {
    const id = setInterval(() => {
      // ...
    });
    setIntervalId(id);
    return () => {
      clearInterval(intervalId);
    };
  });

  // ...
}

두 예 모두 같은 결과를 얻을 수 있지만, 어느 것이 더 나은지, 그 이유는 무엇입니까?

양쪽의 주요 차이점은 다음과 같습니다.

useState의 원인이 됩니다.useRef지지않않않않

사람의 은 둘 useState ★★★★★★★★★★★★★★★★★」useRef재검출 후 데이터를 기억할 수 있습니다. 뷰 렌더링을 에는 뷰 레이어 렌더링을 합니다.useState 않으면 을 사용합니다useRef

저는 이 기사를 읽을 것을 권합니다.

useRef는 값 또는 때 합니다.useEffect의해.

대부분의 사용 사례는 값에 따라 달라지는 함수가 있지만 함수의 결과 자체에 따라 값을 업데이트해야 하는 경우입니다.

예를 들어, 다음과 같은 API 결과에 대한 페이지 수를 지정한다고 가정해 보겠습니다.

const [filter, setFilter] = useState({});
const [rows, setRows] = useState([]);
const [currentPage, setCurrentPage] = useState(1);

const fetchData = useCallback(async () => {
  const nextPage = currentPage + 1;
  const response = await fetchApi({...filter, page: nextPage});
  setRows(response.data);
  if (response.data.length) {
    setCurrentPage(nextPage);
  }
}, [filter, currentPage]);

fetchData를 사용하고 .currentPage.currentPage정상적으로 응답한 후.(무한 루프)가 하기 쉽습니다.Maximum update depth exceeded error되었을 때 같이 .예를 들어 컴포넌트가 로드될 때 행을 가져오려면 다음과 같은 작업을 수행합니다.

useEffect(() => {
  fetchData();
}, [fetchData]);

상태를 사용하고 동일한 기능으로 업데이트하기 때문에 버그가 발생합니다.

하고 currentPage요.useCallback ★★★★★★★★★★★★★★★★★」useEffect그 변화에 의해.

풀 수요.useRef:

const currentPageRef = useRef(0);

const fetchData = useCallback(async () => {
  const nextPage = currentPageRef.current + 1;
  const response = await fetchApi({...filter, page: nextPage});
  setRows(response.data);
  if (response.data.length) {
     currentPageRef.current = nextPage;
  }
}, [filter]);

수 있어요.currentPage입니다.useCallbackuseRef따라서 무한 루프에서 컴포넌트를 절약할 수 있습니다.

useState와 useRef의 주요 차이점은 다음과 같습니다.

  1. 레퍼런스 값은 컴포넌트 재렌더링 간에 유지(동일하게 표시)됩니다.

  2. 를 사용한 참조 useRef컴포넌트 재검출을 트리거하지 않습니다.그러나 상태를 업데이트하면 구성 요소가 다시 렌더링됩니다.

  3. 참조 업데이트는 동기화되며 업데이트된 참조 값은 즉시 사용 가능하지만 상태 업데이트는 비동기입니다. 값은 다시 렌더링된 후에 업데이트됩니다.

코드를 사용하여 보려면:

import { useState } from 'react';
function LogButtonClicks() {
  const [count, setCount] = useState(0);
  
  const handle = () => {
    const updatedCount = count + 1;
    console.log(`Clicked ${updatedCount} times`);
    setCount(updatedCount);
  };
  console.log('I rendered!');
  return <button onClick={handle}>Click me</button>;
}

버튼을 클릭할 때마다 내가 렌더링했다고 표시됩니다!

,에서는useRef

import { useRef } from 'react';
function LogButtonClicks() {
  const countRef = useRef(0);
  
  const handle = () => {
    countRef.current++;
    console.log(`Clicked ${countRef.current} times`);
  };
  console.log('I rendered!');
  return <button onClick={handle}>Click me</button>;
}

는 한 번만 콘솔 로그에 기록될 입니다.

기본적으로 이러한 경우에는 UseState를 사용합니다.이 경우 재렌더링을 통해 상태 값을 갱신해야 합니다.

정보가 컴포넌트 수명 동안 유지되도록 하려면 UseRef를 사용합니다.이것은 재렌더 작업이 아니기 때문입니다.

interval id " interval interval " interval interval 、 " interval id " 。 것은 상태를 입니다.timerActive필요에 따라 타이머를 정지/기동할 수 있습니다.

function Timer() {
  const [timerActive, setTimerActive] = useState(true);

  useEffect(() => {
    if (!timerActive) return;
    const id = setInterval(() => {
      // ...
    });
    return () => {
      clearInterval(intervalId);
    };
  }, [timerActive]);

  // ...
}

모든 렌더에서 콜백을 변경하려면 ref를 사용하여 각 렌더에서 내부 콜백을 업데이트할 수 있습니다.

function Timer() {
  const [timerActive, setTimerActive] = useState(true);
  const callbackRef = useRef();

  useEffect(() => {
    callbackRef.current = () => {
      // Will always be up to date
    };
  });

  useEffect(() => {
    if (!timerActive) return;
    const id = setInterval(() => {
      callbackRef.current()
    });
    return () => {
      clearInterval(intervalId);
    };
  }, [timerActive]);

  // ...
}
  • 「」를하는 앱.useRef하지 않다

useRef를 사용하여 상태를 저장하는 단순 카운터 앱을 만드는 경우:

import { useRef } from "react";

const App = () => {
  const count = useRef(0);

  return (
    <div>
      <h2>count: {count.current}</h2>
      <button
        onClick={() => {
          count.current = count.current + 1;
          console.log(count.current);
        }}
      >
        increase count
      </button>
    </div>
  );
};

하시면, 「 」가 표시됩니다. <h2>count: {count.current}</h2>컴포넌트가 재렌더링이 아니므로 이 값은 변경되지 않습니다. '' ''을 '''console.log(count.current)값이 실제로 증가하고 있는 것을 알 수 있지만 컴포넌트가 재렌더링되지 않기 때문에 UI는 갱신되지 않습니다.

를 '하다'로 useState버튼을 클릭하면 컴포넌트가 다시 렌더링되어 UI가 업데이트됩니다.

  • 중 을 방지합니다.input.

재렌더링은 비용이 많이 드는 작업입니다.경우에 따라서는 앱을 계속 재렌더링하고 싶지 않을 수도 있습니다.예를 들어 입력 값을 상태에 저장하여 제어된 구성 요소를 생성하는 경우입니다.이 경우 키를 누를 때마다 앱을 다시 렌더링합니다.「 」를하고 있는 는,ref 요소를 하려면 , DOM 를 사용합니다.useState컴포넌트를 한 번만 다시 렌더링합니다.

import { useState, useRef } from "react";
const App = () => {
  const [value, setValue] = useState("");
  const valueRef = useRef();
 
  const handleClick = () => {
    console.log(valueRef);
    setValue(valueRef.current.value);
  };
  return (
    <div>
      <h4>Input Value: {value}</h4>
      <input ref={valueRef} />
      <button onClick={handleClick}>click</button>
    </div>
  );
};
  • 를 방지useEffect

간단한 플립 애니메이션을 만들려면 두 개의 상태 값이 필요합니다.하나는 인터벌 내에서 플립 여부를 나타내는 부울값이고, 다른 하나는 컴포넌트를 종료할 때 서브스크립션을 클리어하는 것입니다.

  const [isFlipping, setIsFlipping] = useState(false);      
  let flipInterval = useRef<ReturnType<typeof setInterval>>();

  useEffect(() => {
    startAnimation();
    return () => flipInterval.current && clearInterval(flipInterval.current);
  }, []);

  const startAnimation = () => {
    flipInterval.current = setInterval(() => {
      setIsFlipping((prevFlipping) => !prevFlipping);
    }, 10000);
  };

setInterval하고 ID로 합니다.clearInterval컴포넌트를 탈퇴하면 서브스크립션이 종료합니다. flipInterval.current늘 또는 이 ID.null ID 입니다.★★★★★★★★★★★★★★★★를 사용하지 않는 경우ref여기서 null에서 id 또는 id에서 null로 전환할 때마다 이 컴포넌트가 다시 렌더링되어 무한 루프가 생성됩니다.

  • 를 갱신할 가 없는 를 사용해 .useRef상태 변수를 저장합니다.

예를 들어 리액트 네이티브 앱에서는 UI에 영향을 주지 않는 특정 동작의 사운드를 설정합니다.상태 변수 중 하나는 성능 절감 효과가 크지 않을 수 있지만 게임을 플레이할 경우 게임 상태에 따라 다른 소리를 설정해야 합니다.

const popSoundRef = useRef<Audio.Sound | null>(null);
const pop2SoundRef = useRef<Audio.Sound | null>(null);
const winSoundRef = useRef<Audio.Sound | null>(null);
const lossSoundRef = useRef<Audio.Sound | null>(null);
const drawSoundRef = useRef<Audio.Sound | null>(null);

★★★★★★★★★★★★★★★★를 사용했을 경우.useState상태 값을 변경할 때마다 계속 재렌더링합니다.

이 경우에도 하실 수 있습니다.useRefdom HTML 속성)dom을 하다(HTML

예: 입력 필드에 초점을 맞추는 버튼을 할당합니다.

, 「」입니다.useState는 값만 갱신하고 컴포넌트를 다시 갱신합니다.

실제로 타이머의 사용 목적에 따라 크게 달라지는데, 컴포넌트의 렌더링 내용을 표시하지 않았기 때문에 명확하지 않습니다.

  • 컴포넌트 렌더링에서 타이머 값을 표시하려면 useState를 사용해야 합니다.그렇지 않으면 참조 값이 변경되어 재렌더가 발생하지 않으며 화면에 타이머가 업데이트되지 않습니다.

  • 타이머의 틱에서 UI를 시각적으로 변경해야 하는 다른 문제가 발생할 경우 useState를 사용하여 타이머 변수를 useEffect 훅의 의존관계 배열에 넣거나(UI 업데이트에 필요한 모든 작업을 수행), 타이머 값을 기반으로 렌더 메서드(컴포넌트 반환값)로 로직을 수행합니다.SetState 호출은 재렌더를 발생시킨 후 useEffect 훅을 호출합니다(의존관계 배열에 따라 다름).참조를 사용하면 업데이트가 수행되지 않으며 useEffect가 호출되지 않습니다.

  • 내부적으로만 타이머를 사용하는 경우 대신 useRef를 사용할 수 있습니다.(특정 시간이 경과한 후) 재렌더를 발생시킬 필요가 있는 경우는 항상 setState를 사용하여 setInterval 콜백 내에서 다른 상태 변수를 호출할 수 있습니다.그러면 구성 요소가 다시 렌더링됩니다.

지역 상태에 대한 참조를 사용하는 것은 정말로 필요한 경우에만 수행해야 한다(즉.흐름 또는 성능 문제가 있는 경우)는 "리액트 방식"에 따르지 않기 때문입니다.

useRef()는 UI를 다시 렌더링하지 않고 값만 갱신합니다.UI를 다시 렌더링하려면 useRe 대신 useState()를 사용해야 합니다.수정이 필요하면 알려주세요.

useState.useRef이치노

대부분의 경우 다음과 같은 몇 가지 지침이 도움이 됩니다.

★★★★★★에useState

  • anything with with with와 함께 되는 모든 것inputTextInput에는, 설정하는 값으로 갱신되는 상태가 필요합니다.
  • 하기 위해 useMemo 수 있습니다.useEffect
  • 「수한 경우」는, 「사용할 수 없는 것」의 할 수 . 을 사용법async「」로 useEffect 들면FlatList이치노

★★★★★★에useRef

  • 이벤트 서브스크라이버 등 사용자에게 표시되지 않는 데이터를 저장하기 위해 사용합니다.
  • 훅의 는, 「」나 「커스텀 훅」에 의해서 위해서 합니다.useMemo ★★★★★★★★★★★★★★★★★」useEffect에 의해 useState/useReducer제가 자주 하는 실수는 이런 걸 넣는 거예요.authState상태가 실제로 체인의 최종 결과일 때 전체 렌더를 트리거한다고 업데이트하면 됩니다.
  • ref

차이점은 useState는 현재 상태를 반환하고 상태를 업데이트하는 업데이터 기능이 있다는 것입니다.useRef는 오브젝트를 반환하지만 컴포넌트는 재렌더하지 않고 DOM 요소를 참조하는 데 사용됩니다.

그러므로,

변경 시 재렌더된 뷰를 트리거하는 컴포넌트 상태를 유지하려면 State 또는 useReducer를 사용합니다.상태가 렌더링을 트리거하지 않으려면 useRef로 이동하십시오.

이 예를 보세요.

import { useEffect, useRef } from "react";
import { Form } from "./FormStyle";

const ExampleDemoUseRef = () => {
  const emailRef = useRef("");
  const passwordRef = useRef("");

  useEffect(() => {
    emailRef.current.focus();
  }, []);

  useEffect(() => {
    console.log("render everytime.");
  });

  const handleSubmit = (event) => {
    event.preventDefault();
    const email = emailRef.current.value;
    const password = passwordRef.current.value;
    console.log({ email, password });
  };
  return (
    <div>
      <h1>useRef</h1>
      <Form onSubmit={handleSubmit}>
        <label htmlFor="email">Email: </label>
        <input type="email" name="email" ref={emailRef} />

        <label htmlFor="password">Password: </label>
        <input type="password" name="password" ref={passwordRef} />
        <button>Submit</button>
      </Form>
    </div>
  );
};

export default ExampleDemoUseRef;

이 useState의 예에서는

import { useEffect, useState, useRef } from "react";
import { Form } from "./FormStyle";

const ExampleDemoUseState = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const emailRef = useRef("");

  useEffect(() => {
    console.log("render everytime.");
  });

  useEffect(() => {
    emailRef.current.focus();
  }, []);

  const onChange = (e) => {
    const { type, value } = e.target;

    switch (type) {
      case "email":
        setEmail(value);
        break;
      case "password":
        setPassword(value);
        break;
      default:
        break;
    }
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log({ email, password });
  };

  return (
    <div>
      <h1>useState</h1>
      <Form onSubmit={handleSubmit}>
        <label htmlFor="email">Email: </label>
        <input type="email" name="email" onChange={onChange} ref={emailRef} />

        <label htmlFor="password">Password: </label>
        <input type="password" name="password" onChange={onChange} />
        <button>Submit</button>
      </Form>
    </div>
  );
};

export default ExampleDemoUseState;

따라서 기본적으로는 DOM 요소를 업데이트하지 않고 값을 가져오려면 UseRef를 대신할 수 있습니다(컴포넌트에 상태가 있음).

언급URL : https://stackoverflow.com/questions/56455887/react-usestate-or-useref

반응형