React 데이터 흐름

하향식(top-down) 데이터 흐름

컴포넌트는 컴포넌트 바깥에서 props를 이용해 데이터를 전달받음
단방향 데이터 흐름(one-way data flow)

State로 관리하는 것을 판단하는 기준

부모로부터 props를 통해 전달되는지?

(맞으면 state 아님)

시간이 지나도 변하지 않는지?

(맞으면 state 아님)

컴포넌트 안의 다른 state나 props를 가지고 계산 가능한지?

(맞으면 state 아님)

역방향 데이터 흐름 추가

State 끌어올리기 (Lifting state up)

ex) 하위 컴포넌트의 클릭 이벤트가 부모의 상태를 바꾸어야만 하는 상황 => 새로운 트윗 추가

상위 컴포넌트의 "상태를 변경하는 함수" 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트가 실행한다

Effect Hook

Side Effect

함수 내 어떤 구현이 함수 외부에 영향을 끼치는 경우
React에서는 컴포넌트 내에서 fetch를 사용해 API정보를 가져오거나 이벤트를 활용해 DOM을 직접 조작할 때 Side Effect가 발생했다고 함

let foo = 'hello';

function bar() {
  foo = 'world';
}

bar(); // bar는 side effect를 발생시킴

Pure function (순수 함수)

오직 함수의 입력만이 함수의 결과에 영향을 주는 함수
순수 함수에는 side effect가 없다
어떤 전달 인자가 주어졌을 때 항상 똑같은 값이 리턴되는 것을 보장하기 때문에 예측 가능한 함수임
=> Math.random()이 순수함수가 될 수 없는 이유

function upper(str) {
  return str.toUpperCase(); // toUpperCase는 원본을 수정하지 않는다 = immutable
}

upper('hello') // 'HELLO'

React 컴포넌트에서의 Side Effect

  • 타이머 사용(SetTimeout)
  • 데이터 가져오기 (fetch API, localStorage)

useEffect(함수)

컴포넌트 내에서 Side Effect를 실행할 수 있게 하는 Hook
새롭게 컴포넌트가 렌더링 될 때 Effect Hook 이 실행됨

실행순서

  • 컴포넌트 생성 후 처음 화면에 렌더링(표시)
  • 컴포넌트에 새로운 props가 전달되며 렌더링
  • 컴포넌트에 상태(state)가 바뀌며 렌더링

Hook 쓸 때 주의점

  • 최상위에서만 Hook을 호출
    반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 말 것
    => 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것 보장
    => React가 useStateuseEffect가 여러번 호출되는 과정에서도 Hook의 상태를 올바르게 유지할 수 있도록 해줌
  • React 함수 내에서 Hook을 호출

조건부 effect 발생 (dependency array)

useEffect의 두 번째 인자는 배열. 배열은 조건(어떤 값의 변경이 일어날 때)을 담고 있음.

단 한 번만 실행되는 Effect 함수

  • useEffect(함수, []) => 빈배열 넣기 => 컴포넌트가 처음 생성될 때만 effect함수가 실행됨
    ex) 처음 단 한번 외부 API를 통해 리소스를 받아오고 더이상 API호출이 필요하지 않을 때
  • useEffect(함수) => 아무것도 넣지 않기 => 컴포넌트가 처음 생성되거나 props가 업데이트되거나 상태(state)가 업데이트 될 때

Data Fetching

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);
  }, []);

  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요청할 때 필터링 한 결과를 받아오는 방법 - 서버에 매번 검색어와 함께 요청하는 경우)

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>;
}

두 방식의 차이점

장점단점
컴포넌트 내부처리HTTP 요청의 빈도 줄일 수 있음브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되므로 클라이언트 부담이 늘어난다
컴포넌트 외부처리클라이언트가 필터링 구현을 생각하지 않아도 된다빈번한 HTTP 요청이 일어나고 서버가 필터링을 처리하기 때문에 서버에 부담이 간다

AJAX 요청 예시

명언제공 API의 엔드포인트 : http://서버주소/proverbs

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

0개의 댓글