React Side effect

·2022년 6월 25일
0

react

목록 보기
7/24
post-thumbnail

Side effect

React의 주요 업무

리액트 라이브러리의 주요 작업은 단언컨데, 사용자의 이벤트에 상호작용하여 컴포넌트를 그에 맞게 렌더링 혹은 리 렌더링 하는 것 입니다. 하지만 앱을 만드는데 있어 프론트엔드가 해야할 부분이 그것만 있는 것은 아니죠. 로컬 저장소 데이터를 저장하거나, 잠정적 에러를 처리하거나, http request 를 보내는 것도 필요합니다.

side effect 는 컴포넌트를 다루는 주요 업무외에 어플리케이션에 필요한 작업을 말합니다. 리액트 자체가 필요한 업무나 관련된 업무는 아니기에, 리액트와는 별개로 독립적으로 운영되어야 하는 부분입니다.

만약 리액트 컴포넌트 안에서 API를 요청하는 함수를 만들었다고 해봅시다. 리액트는 상태에 따라, 해당 컴포넌트를 리렌더링 할텐데, 계속 요청을 반복해서 보내게 되지 않을까요??

useEffect()

useEffect()useState() 와 마찬가지로 별도의 리액트 상태관리 훅 입니다. useState()state 변경 시마다 자동으로 컴포넌트를 리렌더링 하였다면, useEffect()는 특정 종속성을 가질 수 있고, 그 해당 종속성이 변화할 때만 useEffect() 를 실행할 수 있습니다.

useEffect() 무한루프

useEffect() 는 컴포넌트 처음 렌더링 할 때를 제외하고, 특정 종속성에만 호출되는 것을 앞서 설명했습니다. 예를 들어 종속성을 인자로 넣지 않은 체, state 를 변경하는 작업을 실행했다면, 해당 컴포넌트는 무한 루프에 빠지게 됩니다.

  1. 브라우저에 처음 렌더링 될 때, useEffect() 가 호출된다.
  2. useEffect()state 변경으로 컴포넌트가 다시 렌더링 된다.
  3. 다시 리렌더링 되면서, useEffect() 를 또 호출한다.
  4. 2 -> 3 을 계속 반복한다.
const Login = (props) => {
  const [enteredEmail, setEnteredEmail] = useState("");
  const [emailIsValid, setEmailIsValid] = useState();
  const [enteredPassword, setEnteredPassword] = useState("");
  const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);

  useEffect(() => {
    const indentifier = setTimeout(() => {
      console.log("checking form validity");
      setFormIsValid(enteredEmail.includes("@") && enteredPassword.trim().length > 6);
    }, 500);

    // cleanup function
    return () => {
      console.log("cleaning up");
      clearTimeout(indentifier);
    };
  });
 ...
 ...
}

무한루프 해결하기

해결방법은 종속성 인자를 빈 배열 형태로 넣는 것 입니다. 이렇게 하면 해당 useEffect() 는 처음 렌더링 할 때 외에는 절대 호출되지 않는 함수가 됩니다.

const Login = (props) => {
  const [enteredEmail, setEnteredEmail] = useState("");
  const [emailIsValid, setEmailIsValid] = useState();
  const [enteredPassword, setEnteredPassword] = useState("");
  const [passwordIsValid, setPasswordIsValid] = useState();
  const [formIsValid, setFormIsValid] = useState(false);

  useEffect(() => {
    const indentifier = setTimeout(() => {
      console.log("checking form validity");
      setFormIsValid(enteredEmail.includes("@") && enteredPassword.trim().length > 6);
    }, 500);

    // cleanup function
    return () => {
      console.log("cleaning up");
      clearTimeout(indentifier);
    };
  }, []);
 ...
 ...
}

어떤 걸 종속성으로 넣으면 안되는 것인가??

useEffect()의 종속성으로는 값이 바뀌는 거라면 어떤 것이든 추가하여 넣는 것이 가능합니다. 즉, 모든 상태 변수, 함수도 가능하다는 것입니다. 따라서 사용할 때는 적절한 종속성을 골라 넣는 것이 매우 중요합니다. 지금부터 나열되는 것들은 수업에서 강조하거나, 리액트에서 권장하거나, 개발자들 사이에서 통용되는 관례적인 부분입니다. 다소 편향된 얘기 일 수 있습니다.

  • useState 의 상태 업데이트 함수 : 리액트는 state를 통해 해당 데이터가 직접적으로 변경되지 않도록 보장합니다. 종속성으로 추가할 필요가 없습니다.
  • 브라우저 API : 브라우저 API는 전역적으로 사용이 가능하며, 리액트의 라이프 사이클과는 전혀 관련이 없고, 변경되지도 않습니다.
  • 별도의 파일에서 import 한 함수나 변수, 컴포넌트 외부에 존재하는 함수 : 이러한 함수 또는 변수 역시 리액트의 컴포넌트 안에서 생성되는 것들이 아니기 때문에 리액트 라이프사이클의 영향을 받지 않아, 변경하더라도 구성 요소에 영향을 주지 않습니다. (정확히는 해당 종속성 자체가 변하더라도, 리액트가 변경됬는지를 파악하지 못해 useEffect() 가 호출되지 않습니다.)

간단히 말하자면, 리액트 라이프사이클의 영향을 받으며, 상태가 변화 할 때마다 컴포넌트가 다시 렌더링 되게 만드는 것들을 종속성으로 추가하는 것이 좋습니다.

Cleanup 함수

useEffect()는 해당 종속성이 변화할 때 마다, 해당 이벤트가 호출됩니다. 만약 이벤트 가운데, 상태가 바뀔 때 마다 나타날 필요 없이, 사용자 요청 마지막에만 한번만 적용하여 바꾸면 훨씬 효율적이지 않을까요??? 어떻게 하면 그게 가능할까요??
정답은 해당 useEffect()의 이벤트가 적용되기 전, 해당 이벤트를 return 시켜 버리면 됩니다.

이러한 작업을 디바운싱이라고 합니다!


// 사용자의 입력 후, 500ms 이후, 유효성 검사를 적용함
// cleanup 함수를 통해, 이벤트 적용 전, 이벤트 무효화
// 결과적으로 여려번 요청이 들어오는 경우, 마지막 한번의 이벤트만 적용되어 호출되게 됨
  useEffect(() => {
    const indentifier = setTimeout(() => {
      console.log("checking form validity");
      setFormIsValid(enteredEmail.includes("@") && enteredPassword.trim().length > 6);
    }, 500);

    // cleanup function
    return () => {
      clearTimeout(indentifier);
    };
  }, [enteredEmail, enteredPassword]);
profile
새로운 것에 관심이 많고, 프로젝트 설계 및 최적화를 좋아합니다.

0개의 댓글