2020.코딩일지

[React-Effect Hook]useEffect() 본문

JS & React

[React-Effect Hook]useEffect()

개발하는라푼젤 2022. 8. 2. 07:08
728x90

useEffect() 언제 실행되나?

useEffect()  언제 실행되나?

컴포넌트 생성 후 처음 화면에 렌더링할 때,

컴포넌트에 새로운 props가 전달되며 렌더링될 때,

컴포넌트에 상태(state)가 바뀌며 렌더링 될 때,

--> 매 번 새롭게 컴포넌트가 렌더링 될 때! Effect Hook 실행된다.

 

**Hook사용시 주의점

최상위에서만 Hook호출하기.

React함수 내에서 Hook을 호출하기.

 


Effect Hook의 기본(예시)-명언제조

 

[명언제조]버튼을 클릭할때마다, 랜덤으로 명언이 나오며, 크롬의 타이틀 부분도 같이 변경된다.

더보기
import { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const proverbs = [
    "좌절감으로 배움을 늦추지 마라",
    "Stay hungry, Stay foolish",
    "Memento Mori",
    "Carpe diem",
    "배움에는 끝이 없다"
  ];
  const [idx, setIdx] = useState(0);

  const handleClick = () => {
    setIdx(idx === proverbs.length - 1 ? 0 : idx + 1);
  };

  return (
    <div className="App">
      <button onClick={handleClick}>명언 제조</button>
      <Proverb saying={proverbs[idx]} />
    </div>
  );
}
//-----------------------------------
function Proverb({ saying }) {
  useEffect(() => {
    document.title = saying;
  });
  return (
    <div>
      <h3>오늘의 명언</h3>
      <div>{saying}</div>
    </div>
  );
}

Effect Hook의 조건부실행

[]빈배열넣으면 컴포넌트 생성시 최초1회만작동 : 외부 API를 통해 리소스받아오고 더이상 API호출이 필요하지 않을때.

아무것도안넣으면 매번동작(컴포넌트 처음생성, props업데이트시, state업데이트시 마다마다)

 

(예시)-useEffect()는언제작동할까요? 

더보기
import { useEffect, useState } from "react";
import "./styles.css";
import { getProverbs } from "./storageUtil";

export default function App() {
  const [proverbs, setProverbs] = useState([]);
  const [filter, setFilter] = useState("");
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("언제 effect 함수가 불릴까요?");
    const result = getProverbs(filter);
    setProverbs(result);
  }, [filter]); //filter만 주시하고있다. 카운터를 눌러도 useEffect는 작동하지않는다.

  const handleChange = (e) => {
    setFilter(e.target.value);
  };

  const handleCounterClick = () => {
    setCount(count + 1);
  };

  return (
    <div className="App">
      필터
      <input type="text" value={filter} onChange={handleChange} />
      <ul>
        {proverbs.map((prvb, i) => (
          <Proverb saying={prvb} key={i} />
        ))}
      </ul>
      <button onClick={handleCounterClick}>카운터 값: {count}</button>
    </div>
  );
}
//-----------------------------------
function Proverb({ saying }) {
  return <li>{saying}</li>;
}

컴포넌트 내에서 AJAX요청

목록 내 필터링을 구현하기 위해서는 2가지 접근방법이 있다.

1. 컴포넌트 내에서 필터링 : 전체 목록 데이터를 불러오고, 목록을 검색어로 filter하는 방법

더보기

컴포넌트 내에서 필터링

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

export default function App() {
  const [proverbs, setProverbs] = useState([]);
  const [filter, setFilter] = useState("");

  useEffect(() => {
    console.log("언제 effect 함수가 불릴까요?");
    const result = getProverbs();
    setProverbs(result);
  }, []); //최초1번만실행

  const handleChange = (e) => {
    setFilter(e.target.value);
  };

  return (
    <div className="App">
      필터
      <input type="text" value={filter} onChange={handleChange} />
      <ul>
        {proverbs
          .filter((prvb) => {
            return prvb.toLowerCase().includes(filter.toLowerCase());
          })
          .map((prvb, i) => (
            <Proverb saying={prvb} key={i} />
          ))}
      </ul>
    </div>
  );
}

function Proverb({ saying }) {
  return <li>{saying}</li>;
}

2. 컴포넌트 외부에서 필터링: 컴포넌트 외부로 API요청을 할 때, 필터링한 결과를 받아오는 방법

(보통, 서버에서 매번 검색어와 함께 요청하는 경우)

더보기

App.js

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

export default function App() {
  const [proverbs, setProverbs] = useState([]);
  const [filter, setFilter] = useState("");
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("언제 effect 함수가 불릴까요?");
    const result = getProverbs(filter);
    setProverbs(result);
  }, [filter]); //필터칸에 검색어 입력할때만!

  const handleChange = (e) => {
    setFilter(e.target.value);
  };

  const handleCounterClick = () => {
    setCount(count + 1);
  };

  return (
    <div className="App">
      필터
      <input type="text" value={filter} onChange={handleChange} />
      <ul>
        {proverbs.map((prvb, i) => (
          <Proverb saying={prvb} key={i} />
        ))}
      </ul>
      <button onClick={handleCounterClick}>카운터 값: {count}</button>
    </div>
  );
}

function Proverb({ saying }) {
  return <li>{saying}</li>;
}

 

 

storageUtil.js

localStorage.setItem(
  "proverbs",
  JSON.stringify([
    "좌절감으로 배움을 늦추지 마라",
    "Stay hungry, Stay foolish",
    "Memento Mori",
    "Carpe diem",
    "배움에는 끝이 없다"
  ])
);

export function getProverbs(filterBy = "") {
  const json = localStorage.getItem("proverbs");
  const proverbs = JSON.parse(json) || [];
  return proverbs.filter((prvb) =>
    prvb.toLowerCase().includes(filterBy.toLowerCase())
  );
}

두 방식의 차이점 : 누가누가 부담스럽냐! 누가 더 부담스러운게 낫냐ㅋㅋㅋ

  장점 단점
컴포넌트
내부에서 처리
HTTP 요청의 빈도 감소 브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되므로, 클라이언트가 부담스럽..
컴포넌트
외부에서 처리
클라이언트가 필터링 구현을 
생각하지 않아도 됨
빈번한 HTTP요청이 일어나며,  서버가 필터링을 처리하므로
서버가 부담스럽..

 

--->AJAX요청을 보내자!!

서버가 필터링하는 (storageUtil.js)대신, fetch API

* 외부API접속이 느릴경우도 있어서... 로딩화면 구현은 필수적이다! (loading indicator)

  const [isLoading, setIsLoading] = useState(true);

  //생략, LoadingIndicator컴포넌트 별도 구현했음을 가정
  return {isLoading ? <LoadingIndicator /> : <div>로딩완료화면</div> }

  useEffect(() => {
    setIsLoading(true);
    fetch(`http://서버주소/proverbs?q=${filter}`)
      .then(resp => resp.json())
      .then(result => {
        setProverbs(result);
        setIsLoading(false);
    });
  }, [filter]);


(미래의 나를위해ㅋ)

클라이언트가 서버에 요청을 덜 보내는 방법

Throttle a series of fetch requests in JavaScript

throffle과 debounce

 

서버가 클라이언트에게 돌려줄 응답을 캐싱하는 방법

HTTP 및 서버 구현에 대한 이해가 필요합니다.

HTTP caching

 

 

Comments