React (3) 상태

ysh·2023년 10월 4일
0

인턴십

목록 보기
12/25

상태 (State)

: 화면의 렌더링을 하기 위한 데이터
: 데이터가 바뀌면 화면이 바뀐다
: useState, useEffect (Hooks)
명령형 프로그래밍

  • 작동 순서를 매번 입력해야 함 (or 함수 사용)

    '안녕'을 찾아 'hi'로 바꾸기
    -> hellodiv 찾아서
    -> innterText를 '하이'

선언형 프로그래밍

  • 미리 작동 방법을 입력해두고 데이터만 변경한다.

    '안녕' -> 'hi'

JS 구조 분해 할당

// 구조 분해 할당
// 배열을 변수로 다시 할당할 수 있다.
// 배열은 순서가 중요, 변수명은 직접 정함.
const arr = [1, 2];
const [one, two] = arr;
console.log(one);
console.log(two);

// 오브젝트도 가능.
// 오브젝트는 순서가 상관 X, 변수 명이 중요함.
const obj = {
  name : "홍길동",
  age : 12,
};
const {name, age} = obj;
console.log(name);
console.log(age);

useState

선언형 프로그래밍이 가능해짐.
선언만 해놓으면, 알아서 데이터가 찾아서 들어감.

import { useState } from "react";

function App() {
    // useState는 리액트에서 상태를 관리하는 함수.
    // 리턴 값은 배열이고,
    // 배열의 첫 요소는 값, 두번째 요소는 값을 변경하는 함수이다.
    // useState의 매개변수는 값의 초기값 (현재 0)
    const [count, setCount] = useState(0);

    const increase = () => {
        // 매개변수로 함수와 값 그 자체를 넣을 수 있다.
        // 함수 추천.
        setCount(prev => prev + 1);
        // setCount(count + 1);
    }

  return (
    <div>
      <div>{count}</div>
      <button onClick={increase}>+</button>
    </div>
  );
}

export default App;

setStatus에 매개변수로 함수를 넣는 게 좋은 이유

setCount는

const increase = () => {
        // 매개변수로 함수와 값 그 자체를 넣을 수 있다.
        // 함수 추천.
        setCount(prev => prev + 1);
        setCount(prev => prev + 1);

        // setCount가 포함된 함수가 실행이 끝나야 set이 한 번 실행됨.
        // setCount(count + 1); // count가 1에서 2가 되는 게 아님.
        // setCount(count + 1); // 여기 넘어올 떈 count는 1 (set이 되지 않음)
        // 즉 끝날 때는 count = 2.
    }

상태를 이용한 검색 기능

item.js - map을 사용해 반복할 li 태그 컴포넌트.

function Item(props){
    return (
        <li>{props.data}</li>
    );
}

export default Item;

App1.js

  • input 태그에서 값이 변할 때마다 호출되는 onChange에 값 변경 함수를 넣어, value 값을 연동.
  • 그리고 해당 값이 포함된 요소만 배열에서 필터링하여 출력.
  • useState로 선언된 변수의 값이 바뀔때마다 App1()이 다시 호출(index.js에서 재렌더링)한다.
  • 여기서 우리가 원하는 것은 '검색어'로 게임리스트에서 '게임'을 찾는 것 (바뀌는 데이터 : 검색어, 게임)
import { useState } from "react";
import Item from "./Item";

function App1(){

    const [searchValue, setSerachValue] = useState("");
  
    const randomNumber = Math.random();
  
    const gameList = ["메이플스토리", "리그오브레전드", "서든어택", "배틀그라운드"];

    // 함수를 빼는 걸 선호. (태그가 길어져서 그런가?)
    const changeSearchValue = event => {
        setSerachValue(prev => event.target.value)
    }

    return <div>
        <div>{randomNumber}</div>
        <input value={searchValue} placeholder="검색어를 입력해주세요" onChange={changeSearchValue}/>
        <ul>{gameList.filter(el => el.includes(searchValue)).map((el, index) => <Item key={index} data={el}/>)}</ul>
    </div>
}

export default App1;


  • 이런 식으로 값이 한 번 들어올 때마다 재렌더링되어,
    리스트가 필터링되고 randomNumber 변수가 새로 생성된다.
  • 위와 같이 간단한 변수를 하나 새로 만드는 것은 빨라서 상관은 없지만,
    원칙상 컴포넌트 안의 모든 변수는 useState를 이용해 선언하는 것이 좋다.

App2.js

  • App1 컴포넌트에서 값이 바뀌어 재렌더링 되어도 바뀔 필요가 없는 부분을 App2.js로 만들었다.
function App2(){
    const randomNumber = Math.random();
    return(
        <div>
            <hr />
            <div>두번째 컴포넌트</div>
            <div>{randomNumber}</div>
        </div>
    );
}

export default App2;


  • App1 부분은 바뀌었지만, App2 부분은 바뀌지 않았다.

App1.js 주석 추가

import { useEffect, useState } from "react";
import Item from "./Item";

function App1(){
    // state가 변경되면 index.js에서 호출한 App1 컴포넌트를 새로 호출한다.
    const [searchValue, setSerachValue] = useState("");

    // 즉, state 변경 마다 App1() 함수가 실행(렌더링)되고,
    // randomNumber의 값도 바뀌게 된다.
    const randomNumber = Math.random();

    // 그래서 렌더링 될 때마다 변경되면 안되는 변수는,
    // 이렇게 state로 변수를 설정해야 한다.
    // const [randomNumber] = useState(Math.random());

    // 원칙상, 컴포넌트 내부 변수는 state로 만드는 것이 좋다.
    // 하지만, 변수를 새로 만드는 것이 state로 설정하는 것보다 빠를 수 있고
    // 그 경우에는 고정적으로.
    const gameList = ["메이플스토리", "리그오브레전드", "서든어택", "배틀그라운드"];

    // 함수를 빼는 걸 선호. (태그가 길어져서 그런가?)
    const changeSearchValue = event => {
        setSerachValue(prev => event.target.value)
    }
    
    return <div>
        <div>{randomNumber}</div>
        <input value={searchValue} placeholder="검색어를 입력해주세요" onChange={changeSearchValue}/>
        <ul>{gameList.filter(el => el.includes(searchValue)).map((el, index) => <Item key={index} data={el}/>)}</ul>
    </div>
}
export default App1;

정리

useState로 선언한 변수, 즉 '상태'가 변경 시마다 재 렌더링,
그래서 특정 데이터가 필요한 영역을 구분지어 컴포넌트를 짜고, 그 컴포넌트만 재 렌더링 되도록 한다.
이게 React의 역할.

실습 : 영화진흥위원회 오픈API에서 영화 리스트 가져와 검색하기

rest api 요청 주소
https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt=20230930
(2023년 9월 30일 데이터)

useEffect

  • 특정 상태가 바뀔 때마다 함수를 실행한다.
    useEffect(실행할 함수, 감지할 상태 리스트);
// searchValue가 바뀔 때마다 함수가 실행.
useEffect(() => {
  console.log(searchValue);
}, [searchValue]);

  • 하지만 렌더링 이후 꼭 한 번은 실행됨(페이지 열릴 때 실행)
// 렌더링 이후 실행되는 훅스
// 페이지가 열릴 때 딱 한 번만 실행됨.
// useEffect(실행할 함수, 감지할 상태 리스트);
useEffect(() => {
  // console.log("통신");
  fetch("https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt=20230930")
    .then((res) => res.json())
    .then((result) => {
    console.log(result)
    setMovieList(prev => result.boxOfficeResult.dailyBoxOfficeList);
  })
    .catch((error) => {
    console.log(error)
  });
}, []);
  • 위처럼 감지할 상태 리스트를 비워두면, 렌더링 이후 한 번만 실행되게 가능.
  • 외부 데이터 가져오기나 페이지가 열릴 때만 실행될 기능을 적어놓으면 됨.
  • 전체 코드
import { useEffect, useState } from "react";
import Item from "./Item";

function App1(){
    // state가 변경되면 index.js에서 호출한 App1 컴포넌트를 새로 호출한다.
    const [searchValue, setSerachValue] = useState("");

    // 즉, state 변경 마다 App1() 함수가 실행(렌더링)되고,
    // randomNumber의 값도 바뀌게 된다.
    const randomNumber = Math.random();

    // 그래서 렌더링 될 때마다 변경되면 안되는 변수는,
    // 이렇게 state로 변수를 설정해야 한다.
    // const [randomNumber] = useState(Math.random());

    // 원칙상, 컴포넌트 내부 변수는 state로 만드는 것이 좋다.
    // 하지만, 변수를 새로 만드는 것이 state로 설정하는 것보다 빠를 수 있고
    // 그 경우에는 고정적으로.
    const [movieList, setMovieList] = useState([]);

    // 함수를 빼는 걸 선호. (태그가 길어져서 그런가?)
    const changeSearchValue = event => {
        setSerachValue(prev => event.target.value)
    }

    // 렌더링 이후 실행되는 훅스
    // 페이지가 열릴 때 딱 한 번만 실행됨.
    // useEffect(실행할 함수, 감지할 상태 리스트);
    useEffect(() => {
        // console.log("통신");
        fetch("https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt=20230930")
        .then((res) => res.json())
        .then((result) => {
            console.log(result)
            setMovieList(prev => result.boxOfficeResult.dailyBoxOfficeList);
        })
        .catch((error) => {
            console.log(error)
        });
    }, []);

    // searchValue가 바뀔 때마다 함수가 실행.
    useEffect(() => {
        console.log(searchValue);
    }, [searchValue]);
    
    return <div>
        <div>{randomNumber}</div>
        <input value={searchValue} placeholder="검색어를 입력해주세요" onChange={changeSearchValue}/>
        <ul>{movieList.filter(el => el.movieNm.includes(searchValue)).map((el, index) => <Item key={index} data={el.movieNm}/>)}</ul>
    </div>
}

export default App1;
profile
유승한

0개의 댓글

관련 채용 정보