[TIL 31] React Hooks: useEffect

yezo cha·2021년 6월 28일
0

React

목록 보기
4/8
post-thumbnail

React 데이터 흐름

React의 개발 방식의 가장 큰 특징은 페이지 단위가 아닌, 컴포넌트 단위로 시작한다는 점이다 !

먼저 컴포넌트를 만들고, 페이지를 조립 해나간다.
즉, 상향식(bottom-up)으로 앱을 만든다. 이것의 가장 큰 장점은 테스트가 쉽고 확장성이 좋다.

하나의 컴포넌트는 한가지 일만 한다.
데이터를 전달하는 주체부모 컴포넌트 ! : 데이터 흐름이 하향식(top-down)이다.
-> 단방향 데이터 흐름(one-way data flow)

상태는 최소화하는 것이 가장 좋다.
상태가 많아질수록 애플리케이션은 복잡해지기 때문이다.
어떤 데이터를 상태로 두어야 하는지 여부는 다음 세가지 질문을 통해 판단하자.

  • 부모로부터 props를 통해 전달되는가? 그러면 확실히 state가 아니다.
  • 시간이 지나도 변하지 않나 ? 그러면 확실히 state가 아니다.
  • 컴포넌트 안의 다른 state나 props를 가지고 계산 가능한가? 그렇다면 state가 아니다.

State 끌어올리기(Lifting state up)

부모 컴포넌트에서의 상태가 하위 컴포넌트에 의해 변하는 경우 -> State 끌어올리기(Lifting state up)
상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다
마치 콜백 함수를 사용하는 방법과 비슷하다.

Twittler React - Lifting State Up Hooks

Effect Hook

리액트 공식 문서

Hook은 클래스 컴포넌트를 작성하지 않아도 state와 같은 특징들을 사용할 수 있다.
Effect Hook을 사용하면 함수 컴포넌트에서 side effect를 수행할 수 있다.

Side Effect ?

React 컴포넌트가 화면에 렌더링 된 이후에 비동기로 처리되어야 하는 부수적인 효과들을 흔히 Side Effect라고 말한다.

앞서 배운 React의 함수 컴포넌트는, props가 입력으로, JSX Element가 출력으로 나간다. 여기에는 그 어떤 Side Effect도 없으며, 순수 함수로 작동된다.

하지만 보통 React 애플리케이션을 작성할 때에는, AJAX 요청이 필요하거나, 어떤 데이터를 가져오기 위해서 외부 API를 호출하는 경우 가 발생할 수 있다. 이는 전부 Side Effect 이다.
왜냐면 이것은 다른 컴포넌트에 영향을 줄 수도 있고, 렌더링 과정에서는 구현할 수 없는 작업이기 때문이다.

React는 Side Effect를 다루기 위한 HookEffect Hook을 제공한다.
Effect Hook, 즉 useEffect는 함수 컴포넌트 내에서 이런 Side Effects를 수행할 수 있게 해준다.

useEffect Hook을 기존의 클래스 컴포넌트를 사용해봤다면 생명주기 함수(lifeCycle methods)와 같은 효과를 낸다고 생각하면 된다.
componenetDidMountcomponentDidUpdate, componentWillUnmount가 합쳐진 것으로 생각하자.

useEffect ??

useEffect는 단어 그대로 함수 컴포넌트에서 사이드 효과들(Side Effects)을 실행하는 것이다.
컴포넌트나 state에 변화가 생길 때 호출되는 함수라고 생각하자.

두 개의 인자를 받는데 첫번째 인자는 변경시 호출할 콜백 함수고, 두번째 인자는 상태 변경을 감지할 state를 설정한다.
state를 별도로 설정하지 않으면 componentDidUpdate, componentDidMount랑 동일한 역할을 하게 된다.

  • useEffect가 하는 일은 뭘까 ?
    useEffect Hook을 사용해서 리액트 컴포넌트가 렌더링 이후에 어떤 일을 수행해야하는 지를 말한다.
  • useEffect를 컴포넌트 안에서 불러내는 이유 ?
    useEffect를 컴포넌트 내부에 둠으로써 effect를 통해 count state 변수(또는 어떤 prop에도)에 접근할 수 있게 된다.
    함수 범위 안에 존재하기 때문에 특별한 API 없이도 값을 얻을 수가 있다.
    (자바스크립트의 클로저를 이용)
  • useEffect는 렌더링 이후에 매번 수행되는걸까 ?
    맞다. 기본적으로 첫번째 렌더링과 이후의 모든 업데이트에서 수행된다.
    마운팅과 업데이트라는 방식으로 생각하는 대신 effect를 렌더링 이후에 발생하는 것으로 생각하자.
const UseEffectExample = () => {
  const [value, setValue] = useState('initial value');
  useEffect(() => {
    console.log('렌더링!');
  });

  return (
    <div>
      <p>{value}</p>
      <input onChange={(e) => {setTest(e.target.value)}} />
    </div>
  );
};

해당 컴포넌트가 렌더링 될 때마다 useEffect함수가 실행되면서 콘솔에 로그를 남긴다.
기존의 state처럼, state의 값이 변경될 경우 렌더링을 시도하기 때문에 input에 타자를 칠 때마다 로그가 찍히게 된다.

state 특정하기

만약 state가 여러개이고, useEffect 함수가 그 state들이 변경될 때마다 호출된다면 매우 비효율적이다.
이런 상황을 위해서 useEffect는 두번째 매개변수 인자를 받을 수 있다.

useEffect(() => {
  console.log('렌더링 !');
},[value]);

이렇게 배열 형태 로 특정 state를 넘기게 되면, 해당 state의 값이 변경될 때만 useEffect함수가 호출되게 된다.

이제 여러 개의 state에 대해서 개별적인 동작을 실행시켜 보자.

useEffect(() => {
  console.log('value1 state에 대해서만 호출해 !');
},[value]);

useEffect(() => {
  console.log('value2 state에 대해서만 호출해 !');
},[value2]);

첫 렌더링에만 호출하기

처음 렌더링 될 때 한번만 실행하고 싶은 경우에는 매개변수를 빈 배열로 넘겨주면 된다.

useEffect(() => {
  console.log('첫 렌더링 호출 !');
},[]);

이렇게 할 경우 최초 한번만 호출된다. 이후의 어떤 렌더링에도 재호출 되지 않는다.

요약

const NoteApp = () => { 
  const [notes, setNotes] = useState([]);
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  
  // 1
  useEffect(() => { 
    console.log('load data') 
    const notesData = JSON.parse(localStorage.getItem('notes'));
    if (notesData) {
      setNotes(notesData)
    } 
  }, []);
  
  // 2
  useEffect(() => {
    console.log('update notes');
    const toJson = JSON.stringify(notes);
    localStorage.setItem('notes', toJson)
  }, [notes]);
  
  // 3
  useEffect(() => {
    console.log('useEffect called') 
  });
  • 1번 useEffect
    두 번째 인자에 빈 배열을 넣어줌 : 최초 한번만 호출.
  • 2번 useEffect
    notes의 상태값을 인자로 넣어줌 : notes의 상태값이 변경될 때마다 함수가 호출됨.
  • 3번 useEffect
    전달인자를 따로 넣지 않아서 내부에서 어떤 state가 변경되더라도 새롭게 호출됨.

React Hooks 시리즈 2탄 - useEffect

profile
(ง •̀_•́)ง

0개의 댓글