[React] 5. 이벤트 처리, useState, useEffect

ㅎㅎ·2023년 8월 1일
0

React

목록 보기
7/11

Event 처리하기

참고 사이트:
https://legacy.reactjs.org/docs/handling-events.html

  • AppProfile.jsx (1)
import './App.css';
import Avartar from './components/Avartar';
import Profile from './components/Profile';

function AppProfile() {
  return (
    <>
      <button onClick={(event) => {
        console.log(event);
        alert("버튼이 클릭됨")
      }}>버튼</button>
      <Avartar image='https://images.unsplash.com/photo-1574158622682-e40e69881006?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=580&q=80' isNew={true}/>
      <Profile
        image='https://images.unsplash.com/photo-1574158622682-e40e69881006?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=580&q=80'
        name='Hana'
        title='프론트엔드 개발자'
        isNew={true}
      />
      <Profile
        image='https://images.unsplash.com/photo-1583512603805-3cc6b41f3edb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=880&q=80'
        name='Anna'
        title='백엔드 개발자'
      />
      <Profile
        image='https://plus.unsplash.com/premium_photo-1661903055250-d4cd3d70f805?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=880&q=80'
        name='Bob'
        title='백엔드 개발자'
      />
    </>
    
  );
}

export default AppProfile;

  • AppProfile.jsx (2)
import './App.css';
import Avartar from './components/Avartar';
import Profile from './components/Profile';

function AppProfile() {
  const handleClick = (event) => {
    console.log(event);
        alert("버튼이 클릭됨");
  };
  return (
    <>
      <button onClick={handleClick}>버튼</button>
      <Avartar image='https://images.unsplash.com/photo-1574158622682-e40e69881006?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=580&q=80' isNew={true}/>
      <Profile
        image='https://images.unsplash.com/photo-1574158622682-e40e69881006?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=580&q=80'
        name='Hana'
        title='프론트엔드 개발자'
        isNew={true}
      />
      <Profile
        image='https://images.unsplash.com/photo-1583512603805-3cc6b41f3edb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=880&q=80'
        name='Anna'
        title='백엔드 개발자'
      />
      <Profile
        image='https://plus.unsplash.com/premium_photo-1661903055250-d4cd3d70f805?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=880&q=80'
        name='Bob'
        title='백엔드 개발자'
      />
    </>
    
  );
}

export default AppProfile;

함수를 따로 정의해서 사용할 수도 있다.

이때 소괄호는 쓰면 안 됨. handleClick() Xx
이러면 핸들클릭 함수가 호출되어 실행된 후에 리턴되는 값이 넘어오는 것

내부 상태 관리 State

변경 가능 한 데이터를 리액트 라이브러리에게 알려줘야함

const [number, setNumber] = useState(0)

유즈스테이트 안에는 초기값

ㄴ 배열이 리턴됨 선택값을 접근할수 있는 변수와 업데이트 할 수 있는 함수 2개가 리턴됨 통상적으로 함수에 set을 붙임

  • Counter.jsx
import React, { useState } from 'react';

export default function Counter() {
    const [count, setCount] = useState(0)
    return (
        <div className='counter'>
            <span className='number'>{count}</span>
            <button className='button' onClick={()=>{
                setCount(count + 1);
            }}>Add +</button>
        </div>
    );
}

근데 왜 자동으로 import 안 되냐…
ㄴ 바로바로~ 왜냐하면 끝까지 useState를 치면 텍스트로 인식하기 때문이다. 치던 중에 자동완성으로 뜨는 useState를 선택해야 자동으로 import 된다.

useState 유의할 점!

만약 클릭이 발생했을 때 setCount를 5번 호출하면 어덯게 될까 → 1 ?? 왜지?

왜냐하면,

import React, { useState } from 'react';

export default function Counter() {
    const [count, setCount] = useState(0)
    return (
        <div className='counter'>
            <span className='number'>{count}</span>
            <button className='button' onClick={()=>{
                setCount(count + 1);
                setCount(count + 1);
                setCount(count + 1);
                setCount(count + 1);
                setCount(count + 1);
            }}>Add +</button>
        </div>
    );
}

onClick이 실행될 때 스냅샷이 되어 count값이 저장이 된다. 그래서 지금 초기에는 count = 0이라고 저장이 되고, 이 환경이 리콜백함수에 저장된다.

setCount는 계속 0 + 1을 하는 것이다.

그래서 아무리 많이 호출해도 count가 0으로 고정되어있으니까 1만 플러스 된다.

setCount((prev) => prev + 1); : 이전 값에 1을 더한걸 리턴한다.

스냅샷 되는데 외부에서 참조하고 있는게 없음! 이전 상태값을 콜백 값으로 전달 받는데 prev에 처음에는 0이 전달 됨

  • Counter.jsx
import React, { useState } from 'react';

export default function Counter() {
    const [count, setCount] = useState(0)
    return (
        <div className='counter'>
            <span className='number'>{count}</span>
            <button className='button' onClick={()=>{
                // setCount(count + 1);
                setCount((prev) => prev + 1);
                setCount((prev) => prev + 1);
                setCount((prev) => prev + 1);
            }}>Add +</button>
        </div>
    );
}

이렇게 하면 3이 추가 됨!

비동기적으로 예상치 못한 값을 적을 땐 setCount((prev) => prev + 1); 를 사용하는 것이 안전하다.

🔥 카운터 상태 끌어올리기

참고용 코드

직접 해보다 해결되지 않아서 코드 참고로 넘어감

  • Counter.jsx
import React, { useState } from 'react';

export default function Counter({total, onClick}) {
    const [count, setCount] = useState(0);
    return (
        <div className='counter'>
            <div>
                <p className='number'>{count} <span className='total'>/{total}</span></p>
            </div>
            <button
                className='button'
                onClick={()=>{
                    setCount((prev) => prev + 1);
                    onClick();
                }}
            >Add +</button>
        </div>
    );
}

그냥 handleClick으로 함수 자체를 넘겨준건데 왜 외부의 값이 변하는 거지?
ㄴ 함수 안에서 외부의 값을 건드리고 있음!

  • App.css
.total {
  font-size: 1rem;
  margin-left: -1.5rem;
}

컴포넌트로 만들어나갈 때 공통적으로 필요한 데이터는 부모에 놓고 클릭했을 때 부모에서 해결이 되길 원한다면 props로 전달해주면 된다
ㄴ 함수의 주소를 전달해주는 건가봐 그 기능을 주는게 아니라

강의 댓글 중 :
setState를 그대로 전달하는 건 외부에 데이터 변경의 주도권을 넘겨주는 것과 같으니, 콜백함수로 감싸서 넘기고 자식 컴포넌트가 그것을 적절한 시점에 호출만 하도록 해야하는 군요! 그럼 호출하는 입장은 그 기능의 세부 로직은 알지 못하니까요!

깃 사용방법 정리 참고사이트 :
https://wordbe.tistory.com/entry/Git-사용-방법-정리commit-push-pull-request-merge-등

useEffect 생애주기

json을 동적으로 가져와서 사용함

fetch() 방법

비동기, fetch 사용하면 바로 경로 파일에 접근할 수 있음

  • Products.jsx
import React, { useState } from 'react';

export default function Products() {
  const [count, setCount] = useState(0);
  const [products, setProducts] = useState([]);

  fetch('data/products.json') // 비동기
  .then(res => res.json()) // json 형태로 변환 (배열)
  .then(data => {
    console.log('뜨근 데이터 네트워크에서 받아옴');
    setProducts(data);
  })
  return (
    <>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <article>
              <h3>{product.name}</h3>
              <p>{product.price}</p>
            </article>
          </li>
        ))}
      </ul>
      <button onClick={() => setCount((prev) => prev + 1)}>{count}</button>
    </>
  );
}

해당 방법은 계속해서 fetch하고 매우 비효율 적임 : 무한 루프에 빠짐

첫번째만 데이터를 받아오고 다시는 받아오지 않게해야함 이걸 사용하는 것이 useEffect

두번째 인자로 텅빈 배열을 전달해줘야함

화면에서 컴포넌트가 사라질때 리턴되는 함수

  • Products.jsx
import React, { useEffect, useState } from 'react';

export default function Products() {
  const [count, setCount] = useState(0);
  const [products, setProducts] = useState([]);

  useEffect(() => {
    fetch('data/products.json') // 비동기
      .then(res => res.json()) // json 형태로 변환 (배열)
      .then(data => {
        console.log('뜨근한 데이터를 네트워크에서 받아옴');
        setProducts(data);
      });
      return () => {
        console.log('깨끗하게 청소하는 일들을 합니다.')
      }
  }, []);

  return (
    <>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <article>
              <h3>{product.name}</h3>
              <p>{product.price}</p>
            </article>
          </li>
        ))}
      </ul>
      <button onClick={() => setCount((prev) => prev + 1)}>{count}</button>
    </>
  );
}
  • AppProducts.jsx
import React, { useState } from 'react';
import './App.css';
import Products from './components/Products';

export default function AppProducts() {
  const [showProducts, setShowProducts] = useState(true);
  return (
    <div>
      {showProducts && <Products />}
      <button onClick={() => setShowProducts((show) => !show)}>Toggle</button>
    </div>
  );
}

useEffect 제대로 사용하기

다시 요청이 필요할 때가 있음

저 checked 배열이 checked가 변경이 될 떄마다 어쩌구래

처음으로 무거운 일을 처리해야할때 useEffect을 유용하게 사용함

고유한 key

리스트안에 사용되는 자식들은 key라는 값을 전달해줘야하고 고유한 인덱스 값을 가지고 있어야함

이렇게 키값을 부여해주면 뜨지 않음

profile
Backend

1개의 댓글

comment-user-thumbnail
2023년 8월 1일

좋은 글이네요. 공유해주셔서 감사합니다.

답글 달기