React-Hook의 useState와 useEffect
FE BE 개발 메모장/React

React-Hook의 useState와 useEffect

useState()

이전 React에서 상태를 관리할때는 Class 컴포넌트에서 setState를 통해 상태를 구현했었지만, Hook에서는 useState라는 함수를 사용하여 Function 컴포넌트에서도 사용이 가능하다.

 

  • 기본 문법
let [number, setNumber] = useState(0);
let [breath, setBreath] = useState(true);

기본 문법은 비구조화 할당을 사용하 첫번째 요소는 현재 상태, 두번째 요소는 Setter 함수로 동작한다.

const setIncrease = () => {
  setNumber(number + 1);
}

const setDecrease = () => {
  setNumber(number - 1);
}


return (
  <div>
  <button onClick={setIncrease} />
  <button onClick={setDecrease} />
   <p>{number}</p>
  </div>
)

Setter 함수는 파라미터로 전달 받은 값을 최신 상태로 설정해준다.

useEffect()

useEffect는 side-effect를 실행시키는 함수이다.

기존 React에서 Class 컴포넌트의 componentDidMount  componentDidUpdate, componentWillUnmount와 같은 동작을 수행하지만 하나로 통합된 것이다.

 

  • 기본 문법
useEffect(callback, [dependencies]);
  • callback: side-effect 로직을 포함하는 콜백함수이다. React가 화면에 변경 사항을 커밋 후 콜백 함수를 실행시킨다.
  • dependencies: useEffect()에서 두번째 인자로 받은 값이 변할 때 useEffect()함수가 실행된다.

useEffect()의 의존성

1. 모든 렌더링 후 effect가 실행된다.

import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Runs after EVERY rendering
  });  
  
  return(
   ...
  )
}

 

2. 빈배열[ ]일 경우: 초기 렌더링 후 부작용이 한번 실행된다.

import { useEffect } from 'react';

function Greet({ name }) {
  const message = `Hello, ${name}!`;

  useEffect(() => {
    // Runs once, after mounting
    document.title = `Greetings page ${name}`;
  }, []);

  return <div>{message}</div>;
}

useEffect를 한번만 호출하기 위해서는 [ ]을 사용하여 첫 마운트(componentDidMount 역할) 후에 콜백을 한번만 실행한다.

 

// First render
<Greet name="Eric" />   // Side-effect RUNS

// Second render, name prop changes
<Greet name="Stan" />   // Side-effect does NOT RUN

// Third render, name prop changes
<Greet name="Butters"/> // Side-effect does NOT RUN

3. prop또는 state가 존재 [prop, state]: 종속성 값이 변경 될 때만 effect 실행됨

import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // Runs after EVERY rendering
  }, [prop, state]);  
  
  
  return(
   ...
  )
}

 

위의 예제를 다시 가져와서 name이라는 prop이 바뀔 때마다 useEffect()가 실행되게 해보자.

import { useEffect } from 'react';

function Greet({ name }) {
  const message = `Hello, ${name}!`;

  useEffect(() => {
    document.title = `Greetings to ${name}`; 
  }, [name]);

  return <div>{message}</div>;
}

아래 예시와 같이 name이라는 prop에 값을 바꿔 전달해줬을때, 처음의 useEffect()을 실행하고 이후의 렌더링에서는 name 값이 변경된 경우에만 실행된다.

// First render
<Greet name="Eric" />   // Side-effect RUNS

// Second render, name prop changes
<Greet name="Stan" />   // Side-effect RUNS

// Third render, name prop doesn't change
<Greet name="Stan" />   // Side-effect does NOT RUN

// Fourth render, name prop changes
<Greet name="Butters"/> // Side-effect RUNS

데이터 가져오기

useEffect()로 데이터를 가져올때 side-Effect를 수행할 수 있다.

import { useEffect, useState } from 'react';

function FetchEmployeesByQuery({ query }) {
  const [employees, setEmployees] = useState([]);

  useEffect(() => {
    async function fetchEmployees() {
      const response = await fetch(
        `/employees?q=${encodeURIComponent(query)}`
      );
      const fetchedEmployees = await response.json(response);
      setEmployees(fetchedEmployees);
    }
    fetchEmployees();
  }, [query]);

  return (
    <div>
      {employees.map(name => <div>{name}</div>)}
    </div>
  );
}

 

 FetchEmployeesByQuery 컴포넌트는 query의 값에 따라 직원 목록을 필터링 해준다. useEffect() 함수의 첫번재 인자로 콜백함수를 담아야 하기 때문에 async 함수가 될 수 없다. 그렇기 때문에 콜백 함수 내부에서 async를 정의한 다음 호출을 한다.

 

해당 컴포넌트가 초기 마운트이후 uesEffect()의 fetchEmployees() 비동기 함수를 호출하고 요청을 시작한다.

 

요청이 완료되면  응답받은 직원 목록 상태(setEmployees(fetchedEmployees))를 최신상태(employees)로 바꿔준다.

 

만약 가져오기 요청을 단 한번만 실행하려면 useEffect()의 두번째 인자를 빈배열 [ ] 로 작성하면 된다.

 

import { useEffect, useState } from 'react';

function FetchEmployeesByQuery({ query }) {
  const [employees, setEmployees] = useState([]);

  useEffect(() => {
    async function fetchEmployees() {
        ....
    }
    fetchEmployees();
  }, []);

.....

 

CleanUP

cleanup은 기존 react의 생명주기에서 componentWillUnmout를 실행하는 것 과 같다. useEffect()의 콜백함수 내부에서 return 값이 있는 경우 hook에서 다음 effect가 실행되기 전에 실행 해 준다.

 

useEffect(() => {
  // Side-effect...

  return function cleanup() {
    // Side-effect cleanup...
  };
}, dependencies);

정리하자면 다음과 같다.

 

1. 컴포넌트가 마운트된다. 

 

2. 초기 렌더링 후 useEffect()가 한번 실행된다.

 

3. 내부에 return값이 존재해, 다음 useEffect()를 실행하기 전에 실행한다.

 

4.  마운트를 해제한다. 1번으로 돌아가서 query의 값이 바뀔때마다 반복한다.

 

좀더 이해하기 쉬운 예제

import React, { useEffect, useState } from "react";
import "./styles.css";

function RepeatMessage({ message }) {
  useEffect(() => {
    setInterval(() => {
      console.log(message);
    }, 2000);
  }, [message]);

  return <div className="message">I'm logging to console "{message}"</div>;
}

export default function App() {
  const [message, setMessage] = useState("Hello, World!");

  return (
    <div className="App">
      <h3>Type the message to log to console</h3>
      <input
        type="text"
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />
      <RepeatMessage message={message} />
    </div>
  );
}

 

이 컴포넌트는 메세지를 입력 할 때마다 브라우저의 콘솔에 message의 값이 2초마다 기록된다.

 

드미트리 파블루틴 예제-A

 

function RepeatMessage({ message }) {
  useEffect(() => {
    setInterval(() => {
      console.log(message);
    }, 2000);
    
    return () => {
      clearInterval(id)
    }
  }, [message]);

위의 예제에 useEffect() 내부에 cleanup 함수를 넣어 몇가지 메세지를 입력해보자

 

드미트리 파블루틴 예제-A

cleanup함수를 담기 전 까진 message의 값을 바꿔줬을때 모두 콘솔로 기록했지만, cleanup 함수를 작성하고 나서 최근에 작성한 내용만을 기록한다.

 

 

ref: "humanscape","Demitry Pavlutin","",""

'FE BE 개발 메모장 > React' 카테고리의 다른 글

SPA 웹 구성을 위한 React-router-dom  (0) 2021.05.05
React Hook을 알아보자  (0) 2021.03.01
생명주기(Life-Cycle) 1차  (0) 2021.02.28
REACT 데이터의 흐름과 사고  (0) 2021.02.16
State와 Props에 대해서  (0) 2021.02.13