Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

옥수수, 기록

리액트 Lifting state up & useEffect의 사용 본문

카테고리 없음

리액트 Lifting state up & useEffect의 사용

ok-soosoo 2022. 12. 7. 00:14

state가 필요한 때

state가 많아질수록 애플리케이션은 복잡해지기 때문에 모든 데이터를 state로 둘 필요가 없다. 다음 3가지 질문을 통해 이 데이터를 state로 두어야 할지 결정할 수 있다.

  • 부모로부터 props를 통해 전달되면 state로 두면 안된다.
  • 시간이 지나도 변하지 않는 값, state X
  • 컴포넌트 안의 다른 state나 props를 가지고 계산이 가능하다? state X

state를 어디에 위치해야할까?

state가 특정 컴포넌트에서만 유의미하다면 그 컴포넌트에만 두면 되는데 2개의 컴포넌트가 하나의 state를 기반으로 영향을 받는다면 공통 소유 컴포넌트에 state를 위치시켜야 한다.

Lifting state up(State 끌어올리기)

React는 단방향 데이터흐름을 따른다. 하지만 상위컴포넌트의 state값을 하위컴포넌트가 setState함수를 사용해 변경할 일도 있다.

대표적인 예로 트윗추가가 있다. (내용을 입력하고 버튼을 눌렀을 경우 전체트윗에 입력내용 추가 >> onClick 이벤트가 state값을 변경하는 것)

상태를 변경시키는 함수(handler)를 하위 컴포넌트에 props로 전달해서 해결할 수 있다.

state를 직접 전달하는 것이 아닌 state 갱신 함수를 전달 받아 해당 함수를 실행시키는 원리이다.

import React, { useState } from "react";

export default function ParentComponent() {
  const [value, setValue] = useState("change me"); // useState를 사용해 변동하는 값을 관리한다. value의 초기값은 "change me"
  const handleChangeValue = () => { //handleChangeValue 함수를 실행시 setValue 상태변경함수가 실행되며 
    setValue("changed value"); // value를 "보여줄게 완전히 달라진 값" 으로 바꿔준다.
  };

  return (
    <div>
      <div>값은 {value} 입니다</div>
      <ChildComponent handleButtonClick={handleChangeValue}/> // handleChangeValue 함수를 props로 ChildComponent에 넘겨준다
    </div>
  );
}

function ChildComponent({handleButtonClick}) { // props로 handleChangeValue를 받아옴
  const handleClick = () => {
   
    handleButtonClick() //
  };

  return <button onClick={handleClick}>값 변경</button>; // 클릭할 때 handleClick 함수를 실행
}

위와 다르게 필요에 따라 설정할 값을 콜백 함수의 인자로 넘길 수도 있다

  const [value, setValue] = useState("change me");

  const handleChangeValue = (newValue) => { // handleChangeValue 함수의 인자에 넣은 변수를 상태변경함수의 인자로
    setValue(newValue);
  };

  // ...생략...
}

function ChildComponent({ handleButtonClick }) {
  const handleClick = () => {
    handleButtonClick('changed value')
  }

  return (
    <button onClick={handleClick}>값 변경</button>
  )
}

Side Effect(부수효과)

함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다고 얘기한다.

ex)

let variation = 'hi';

function sideEffect() {
  variation = 'changed';
}

sideEffect();
console.log(variation) // 'changed'

Pure Function(순수 함수)

함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미

ex)

function upper(str) {
	return str.toUpperCase(); //원본을 수정하지 않음(Immutable)
}

마찬가지로 React의 함수 컴포넌트는 props가 입력, JSX Element가 출력으로 가는 Side Effect 없이 순수 함수로 작동한다.

function SingleTweet({ writer, body, createdAt }) {
  return <div>
    <div>{writer}</div>
    <div>{createdAt}</div>
    <div>{body}</div>
  </div>
}

하지만 AJAX요청이 필요하거나 React와 상관없는 API를 사용하는 경우가 있는데 다 Side Effect이다.

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

React는 이럴 때 Side Effect를 다룰 수 있는 Hook인 Effect Hook을 제공한다.

useEffect

컴포넌트 내에서 Side effect를 실행할 수 있게 하는 Hook이다.

useEffect의 첫 번째 인자는 함수이다. 해당 함수 내에서 side effect를 실행하면 된다.

useEffect가 실행되는 때

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

Hook 사용시 주의사항

  • 최상위에서만 Hook을 호출
  • React 함수 내에서 Hook을 호출

사용 예시

useEffect(() => {
	console.log(몇 번 호출될까요?) // 컴포넌트가 처음 생성되거나, props가 업데이트 되거나, state가 업데이트 될 때마다 실행
})

useEffect(() => {
	console.log(몇 번 호출될까요?) // 컴포넌트가 처음 생성될 때만 함수가 실행
},[])

seEffect(() => {
	console.log(몇 번 호출될까요?) // dep이 업데이트 될 때마다 실행
},[dep])

조건부 effect 발생(dependency array)

useEffect의 두 번째 인자는 배열이다. 배열은 조건을 담고 있고 boolean 형태의 표현식이 아닌 어떤 값의 변경이 일어날 때를 의미한다.

해당 배열에는 어떤 값의 목록이 들어가고 종속성 배열이라고 한다.

형태 : useEffect(함수, [종속1, 종속2, …]) >> 종속1의 값이나 종속2의 값, useEffect의 두번째 인자인 배열의 어느 한 요소가 변경되면 useEffect는 호출되게 된다.

Effect 함수가 한 번만 실행되는 때

// 컴포넌트가 처음 생성될 때만 effect 함수가 실행된다.
// 외부 API를 통해 리소스를 받아오고 더 이상 API 호출이 필요하지 않을 때 사용
1. 두번째 인자로 빈배열
useEffect(함수, [])

// 컴포넌트가 
2. 아무것도 넣지않기(기본형태)
useEffect(함수)

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

  • 장점 : HTTP 요청의 빈도를 줄일 수 있다.
  • 단점 : 브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되므로, 클라이언트의 부담이 늘어난다.
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>;
}

컴포넌트 외부에서 필터링 : 1. 컴포넌트 외부로 API 요청을 할 때, 필터링 한 결과를 받아오는 방법 (보통, 서버에 매번 검색어와 함께 요청하는 경우가 이에 해당)

  • 장점 : 클라이언트가 필터링 구현을 생각하지 않아도 된다.
  • 단점 : 빈번한 HTTP 요청이 일어나게 되며, 서버가 필터링을 처리하므로 서버가 부담을 가져간다.
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>;
}

서버에 AJAX 요청을 보내는 예

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