React 공식 문서 스터디 : 4-3, 4-4

Junho Yun·2023년 4월 7일
0

Effect와 동기화하기

목차

  • Effect가 무엇인지
  • Effect와 이벤트의 차이점
  • 컴포넌트에서 Effect를 선언하는 방법
  • 불필요하게 Effect를 재실행하는 것을 건너뛰는 방법
  • 개발시 Effect가 두번 실행되는 이유와 해결 방법

Effect란 무엇이며 이벤트와 차이점이 무엇인가요?

랜더링 코드 vs 이벤트 코드

  • 렌더링 코드 (UI설명에서 소개됨)는 컴포넌트의 최상위 레벨에 있습니다. 여기에는 props와 state를 가져와 변환하고 회면에 표시하려는 JSX를 반환합니다. 렌더링 코드는 순수해야합니다. 수학 공식처럼 결과만 계산할 뿐 다른 작업은 수행하지 않습니다.

  • 이벤트 핸들러(상호작용 추가에서 소개됨)는 컴포넌트 내부에 있는 중첩된 함수로, 계산만 하는 것이 아니라 작업을 수행합니다. 이벤트 핸들러는 입력 필드를 업데이트하거나 HTTP POST요청을 제출하여 제품을 구매하거나 사용자를 다른 화면으로 이동할 수 있습니다. 이벤트 핸들러에는 특정 사용자 작업(예:버튼 클릭 또는 입력)으로 인해 발생하는 ‘사이드 이펙트’(프로그램 상태 변경)가 포함되어 있습니다.

Effect 설명

React에서 Effect는 컴포넌트가 마운트, 업데이트 또는 마운트 해제될 때와 같은 특정 조건이 충족되면 자동으로 실행되는 기능입니다. 효과는 데이터 가져오기, DOM 조작 또는 이벤트 구독과 같은 부작용을 수행하는 데 사용됩니다.

이벤트 설명

반면에 이벤트는 React 구성 요소에서 콜백 함수를 트리거하는 사용자 작업 또는 시스템 생성 발생입니다. 이벤트는 버튼 클릭, 양식 입력 또는 페이지 스크롤과 같은 사용자 상호 작용일 수 있습니다. 또한 서버에서 데이터 수신과 같은 시스템 이벤트나 창 크기 조정과 같은 브라우저 이벤트일 수도 있습니다.

요약하면 Effects는 React 구성 요소에서 특정 조건이 충족될 때 개발자가 부작용을 수행할 수 있는 기능이고 이벤트는 브라우저나 응용 프로그램에서 발생할 때 콜백 기능을 트리거하는 작업 또는 발생입니다.

Effect가 필요하지 않을 수 있습니다. 추후 설명

작성 방법

  1. Effect를 선언합니다. 기본적으로 Effect는 모든 렌더링 후에 실행됩니다.

  2. Effect 종속성을 명시합니다. 대부분의 Effect는 렌더링 할 때마다가 아니라 필요할 때만 다시 실행해야 합니다. 종속성을 지정하여 이를 제어하는 방법을 배웁니다.

  3. 필요한 경우 정리를 추가합니다. 일부 Effect는 수행중이던 작업을 중지, 실행 취소 또는 정리하는 방법을 명시해야합니다. 예를 들어 “connect”에는 “disconnect”가 필요하고 “subscribe”에는 “unsubscribe”가 필요하며 “fectch”에는 “cancel”또는 “ignore”가 필요합니다. 정리 함수를 반환하여 이를 수행하는 방법을 배웁니다.

여러가지 Effect 예시

  1. 종속성이 없는 효과: 이 효과는 모든 렌더링 후에 실행됩니다. 첫 번째 인수로 함수를 사용하고 구성 요소가 마운트, 업데이트 또는 마운트 해제된 후에 실행합니다. 예를 들면 다음과 같습니다.
useEffect(() => {
  console.log('Effect ran!');
});
  1. 종속성이 있는 효과: 이 효과는 하나 이상의 종속성이 변경될 때만 실행됩니다. 종속성은 useEffect의 두 번째 인수로 배열로 전달됩니다. 예를 들면 다음과 같습니다.
useEffect(() => {
  console.log('Effect ran!');
}, [count]);
  1. 정리 효과: 이 효과는 구성 요소가 DOM에서 제거되기 전에 구성 요소가 마운트 해제될 때 실행됩니다. 구성 요소가 만든 모든 리소스를 정리하는 데 유용합니다. 예를 들면 다음과 같습니다.
useEffect(() => {
  // Setup
  return () => {
    // Cleanup
  }
}, []);
  1. cleanup 함수를 반환하는 Effect: 이 Effect는 Cleanup Effect와 유사하지만 Effect에 의해 생성된 리소스를 정리하는 데 사용할 수 있는 함수도 반환합니다. 예를 들면 다음과 같습니다.
useEffect(() => {
  const subscription = someObservable.subscribe();
  return () => {
    subscription.unsubscribe();
  }
}, []);

개발 환경에서 Effect 를 두 번 처리하는 방법은 무엇인가요?

일반적으로 정답은 cleanup 함수를 구현하는 것입니다. cleanup 함수는 Effect가 수행 중이던 작업을 중지하거나 실행 취소해야 합니다. 경험상 사용자가 프로덕션에서와 같이 한 번 실행되는 Effect와 개발 환경에서 볼 수 있는 설정 → 정리 → 설정 시퀀스를 구분할 수 없어야 합니다.

effect 에서 데이터 패칭

Effects 내에서 fetch 호출을 작성하는 것은 특히 완전한 클라이언트 측 앱에서 데이터를 가져오는 인기 있는 방법입니다. 그러나 이것은 매우 수동적인 접근 방식이며 상당한 단점이 있습니다.
Effects는 서버에서 실행되지 않습니다. 즉, 초기 서버 렌더링 HTML에는 데이터가 없는 로드 상태만 포함됩니다. 클라이언트 컴퓨터는 이제 데이터를 로드해야 한다는 사실을 발견하기 위해서만 모든 JavaScript를 다운로드하고 앱을 렌더링해야 합니다. 이것은 그다지 효율적이지 않습니다.
Effects에서 직접 가져오는 것은 일반적으로 데이터를 미리 로드하거나 캐시하지 않는다는 것을 의미합니다. 예를 들어 구성 요소가 마운트 해제되었다가 다시 마운트되면 데이터를 다시 가져와야 합니다.

답안

인기 있는 오픈소스 솔루션에는 React Query, useSWR, 그리고 React Router 6.4+가 있습니다. 자신만의 솔루션을 구축할 수도 있습니다. 이 경우 후드 아래에서 Effects를 사용하지만 요청 중복 제거, 응답 캐싱 및 네트워크 폭포 방지(데이터를 미리 로드하거나 데이터 요구 사항을 경로에 호이스팅하여)를 위한 논리를 추가합니다.

You Might Not Need an Effect

목차

  • 컴포넌트에서 불필요한 이펙트를 제거하는 이유와 방법
  • 이펙트 없이 값비싼 계산을 캐시하는 방법
  • 이펙트 없이 컴포넌트 state를 리셋하고 조정하는 방법
  • 이벤트 핸들러 간에 로직을 공유하는 방법
  • 이벤트 핸들러로 이동되어야 하는 로직
  • 부모 컴포넌트에 변경 사항을 알리는 방법

불필요한 이펙트를 제거하는 방법

  • 렌더링을 위해서 데이터를 변환하는 Effect는 필요하지 않습니다.
function MyComponent() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('https://myapi.com/data')
      .then(response => response.json())
      .then(data => {
        const filteredData = data.filter(item => item.isActive);
        setData(filteredData);
      });
  }, []);

  const mappedData = data.map(item => <Item key={item.id} item={item} />);

  return (
    <div>
      {mappedData}
    </div>
  );
}

비용이 많이 드는 계산을 캐시하려면 useEffect 대신 useMemo를 추가

React에서는 useMemo 후크를 사용하여 값비싼 계산을 메모하고 불필요한 재렌더링을 방지할 수 있습니다. 부작용을 처리하도록 설계된 useEffect 후크와 달리 useMemo 후크는 함수 호출 결과를 캐싱하여 구성 요소의 성능을 최적화하는 데 사용됩니다.

function MyComponent({ data }) {
  const expensiveCalculation = useMemo(() => {
    // This function will only be called when `data` changes
    return data.filter(item => item.isActive);
  }, [data]);

  return (
    <div>
      {expensiveCalculation.map(item => <Item key={item.id} item={item} />)}
    </div>
  );
}

여러 컴포넌트의 state를 업데이트해야 하는 경우 단일 이벤트 중에 수행하는 것이 좋습니다.

단일 이벤트에 대한 응답으로 여러 구성 요소의 상태를 업데이트해야 하는 경우 일반적으로 단일 이벤트 핸들러에서 상태 업데이트를 수행하는 것이 좋습니다. 이 접근 방식은 영향을 받는 모든 구성 요소에서 상태 변경이 원자적이고 일관성이 있는지 확인하는 데 도움이 될 수 있습니다.

import React, { useState } from 'react';

function ComponentA({ count, setCount }) {
  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>ComponentA: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

function ComponentB({ count, setCount }) {
  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>ComponentB: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

function App() {
  const [countA, setCountA] = useState(0);
  const [countB, setCountB] = useState(0);

  const handleClick = () => {
    setCountA(countA + 1);
    setCountB(countB + 1);
  };

  return (
    <div>
      <ComponentA count={countA} setCount={setCountA} />
      <ComponentB count={countB} setCount={setCountB} />
      <button onClick={handleClick}>Increment Both</button>
    </div>
  );
}

이 수정된 예에서 App 구성 요소는 이제 별도의 count 및 setCount 상태 쌍을 사용하여 ComponentA 및 ComponentB 모두의 상태를 관리합니다. "모두 증가" 버튼에 대한 이벤트 핸들러는 setCount에 대한 단일 호출에서 두 count 값을 모두 업데이트합니다.

단일 이벤트 핸들러에서 상태 업데이트를 수행하면 영향을 받는 모든 구성 요소에서 상태 변경이 원자적이고 일관성이 있는지 확인할 수 있습니다. 이는 상태 관리가 더 어려워질 수 있는 더 복잡한 애플리케이션에서 특히 중요할 수 있습니다.

여러 컴포넌트에서 state 변수를 동기화하려고 할 때마다 state 리프팅을 고려하기

상태 리프팅은 구성 요소 트리의 구성 요소 상태를 공통 조상으로 이동하여 액세스해야 하는 다른 구성 요소와 공유할 수 있도록 하는 React의 기술입니다. 이는 여러 구성 요소 간에 상태 변수를 동기화하려는 경우에 자주 수행됩니다.

import React, { useState } from 'react';

function InputField({ label, value, onChange, isValid }) {
  return (
    <div>
      <label>{label}:</label>
      <input type="text" value={value} onChange={onChange} />
      {isValid ? null : <span style={{ color: 'red' }}>Invalid input</span>}
    </div>
  );
}

function Form() {
  const [username, setUsername] = useState('');
  const [email, setEmail] = useState('');
  const [isValid, setIsValid] = useState(true);

  const handleUsernameChange = (event) => {
    setUsername(event.target.value);
  };

  const handleEmailChange = (event) => {
    setEmail(event.target.value);
  };

  const validateForm = () => {
    // perform validation logic here
    const isUsernameValid = username.length > 0;
    const isEmailValid = /\S+@\S+\.\S+/.test(email);
    setIsValid(isUsernameValid && isEmailValid);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    validateForm();
    if (isValid) {
      // submit form
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <InputField
        label="Username"
        value={username}
        onChange={handleUsernameChange}
        isValid={username.length > 0}
      />
      <InputField
        label="Email"
        value={email}
        onChange={handleEmailChange}
        isValid={/\S+@\S+\.\S+/.test(email)}
      />
      <button type="submit">Submit</button>
      {!isValid && (
        <span style={{ color: 'red' }}>Please fix the errors above</span>
      )}
    </form>
  );
}

이 예에서 InputField 구성 요소는 label, value, onChange 및 isValid 소품을 허용하는 HTML 입력 필드 주변의 간단한 래퍼입니다. Form 구성 요소는 username, email 및 isValid 변수의 상태를 관리하고 InputField 구성 요소에 소품으로 전달합니다.

'validateForm' 함수는 양식에 대한 유효성 검사 논리를 수행하고 이에 따라 'isValid' 상태를 업데이트합니다. handleSubmit 이벤트 핸들러는 validateForm을 호출하고 isValid 상태가 true인 경우 양식을 제출합니다.

양식 유효성 검사를 위한 상태 및 논리를 'Form' 구성 요소까지 들어 올리면 모든 입력 필드가 적절하게 유효성 검사되고 양식이 유효하지 않은 경우에만 오류 메시지가 표시되도록 할 수 있습니다. 이것은 특히 입력 필드와 유효성 검사 규칙의 수가 증가함에 따라 코드를 더 간단하고 유지하기 쉽게 만듭니다.

profile
의미 없는 코드는 없다.

0개의 댓글