프론트엔드 데브코스 5기 TIL 47 - 리액트

김영현·2023년 12월 4일
0

TIL

목록 보기
56/129

리액트

Vue도 재밌었지만 실무에서 많이쓰는건 결국 리액트다!
리액트는 반응형 프레임워크임. 헷갈리면 엑셀을 떠올리자 ㅎㅎ

처음엔 컴포넌트만 열심히 생각해보자. 구조, 아키텍처, 아름다운 코드는 잠깐 잊고 UI를 구현해보자!

create-react-app

리액트로 애플리케이션 만들때 가장 빠른방법 => webpack등의 번들링 설정을 모두 대신해준다. 마치 vite같구먼?

JSX

리액트는 JSX를 기본 문법으로 채택함.

//원래 이렇게 작성되는데..
React.createElement('div', {options...})

//마치 html처럼 작성하면 파싱해서 위처럼바꿔준다.
<div></div/>

UI를 추상적으로 바라보기!

공통점을 찾는 것이 재사용의 시작. 제약 없이 계속 추상화하면 곤란.
리액트 컴포넌트는 결국 함수다! 간단하게 설계하고 코딩하다가 리팩토링하자


실습

import logo from "./logo.svg";
import PropTypes from "prop-types";

function Logo({ size }) {
  return (
    <img
      src={logo}
      className="App-logo"
      alt="logo"
      style={{ width: size, height: size }}
    />
  );
}

Logo.defaultProps = {
  size: "100px",
};
Logo.propTypes = {
  size: PropTypes.string,
};

export default Logo;

기본 타입을 지정해줄수도있고 타입을 정해줄수도 있음. 하지만 TS처럼 강제력은 없다...그냥 브라우저 콘솔창에 오류가 남ㅎㅎ

children

특수한 props임. 예약어다. Vueslot기능과 유사하다.

//Paragraph.js
function Paragraph({ children, size }) {
  return <p style={{ fontSize: size }}>{children}</p>;
}

export default Paragraph;

//App.js
...
        <Paragraph>
          Edit <code>src/App.js</code> and save to reload.
        </Paragraph>

이렇게 컴포넌트 사이에 들어온 데이터를 children 프롭스로 받아온다!
참고로 children의 타입은 node

분기와 반복

vue에서는 분기처리를 v-if, v-show로 행했다.
리액트에서는 삼항연산자, 논리연산자를 사용하면 된다.

  const [visible, setVisible] = useState(false);
  return (
    <div className="App">
      <button onClick={() => setVisible(!visible)}>토글</button>
      {visible && <h1>조건부 렌더링</h1>}
    </div>

반복문도 vue와 유사하다

      <ul>
        {articles.map((article) => (
          <li key={article.id}>
            {article.title} | {article.author}
          </li>
        ))}
      </ul>

리액트도 마찬가지로 key프로퍼티를 넣어주어야한다. 리스트에 변경사항이 일어났을때 최적화를 도와준다.
아마 객체처럼 키-밸류로 저장하니 그런거겠지?

부모한테 상태 전달

정확히는 부모가 setState함수를 자식에게 전달하여, 자식이 업데이트된 값을 부모의 상태로 전달하는 것이다.

//App.js
  const [totalCount, setTotalCount] = useState(0);
  return (
    <div>
      {`총합 : ${totalCount}`}
      <Counter onChange={setTotalCount} />
      <Counter onChange={setTotalCount} />
      <Counter onChange={setTotalCount} />
      <Counter onChange={setTotalCount} />
    </div>
  );
//Counter.js
  const [count, setCount] = useState(0);
  const increase = () => {
    setCount(count + 1);
    if (onChange) {
      onChange(count + 1);
    }
  };
  const decrease = () => {
    setCount(count - 1);
    if (onChange) {
      onChange(count - 1);
    }
  };
  return (
    <div style={{ fontSize: "50px" }}>
      {count}
      <button onClick={increase}>+</button>
      <button onClick={decrease}>-</button>
    </div>

$emit을 사용하지 않으니까 더 간결한것 같기도 하고 ㅎㅎ
대신 함수를 전달해줘야하는 점이 다르다.


빌트인 훅

이라는 함수가 리액트에는 내장되어있음. 가령 useState처럼... 그런 훅들을 파헤쳐보자 ㅎㅎ

useEffect

useEffect(setup, dependencies?) 

setup컴포넌트가 마운트될때 일어나는 함수
dependencies는 배열인데, 의존값이 변경되면 useEffect가 재호출됨!

나중에 자세히 다뤄본다!

useRef

useStatestate값이 변경되면 다시 렌더링함.
하지만 useRef는 가져온 값이 변경되도 리렌더링이 일어나지 않는다.

  const inputRef = useRef();
  return (
    <div>
      <input ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
    </div>

이렇게 DOM에 접근할때 사용한다.
또한 컴포넌트 내부의 ref를 가져올수도 있다.

//App.js
  const inputRef = useRef();
  return (
    <div>
      <Input ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
    </div>
  );
//Input.js
const Input = React.forwardRef((_, ref) => {
  return (
    <>
      Input: <input ref={ref} />
    </>
  );
});

그런데 여기서 의문이 생김. refReact.forwardRef라는 키워드 없이 프롭스로 전달하면 안되는걸까?

const Input = ({ refs }) => {
  return (
    <>
      Input: <input ref={refs} />
    </>
  );
};

ref는 예약어라 refs프롭으로 전달해주었다. 이렇게하니 된다. 왜 React.forwardRef를 쓰는거지..??


페이지네이션

라우팅인줄 알았는데, 콘텐츠를 페이지별로 나눠놓는것이다(저번 과제에서 영화 목록 받아올때 페이지별로 받아왔다).

//App.js
  const [page, setPage] = useState(0);
  const articles = new Array(100)
    .fill()
    .map((_, i) => ({ id: i, title: `${i}번 게시물` }));
  const limit = 10;
  const offset = page * limit;
  return (
    <div>
      <Pagination
        defaultPage={0}
        limit={limit}
        total={articles.length}
        onChange={setPage}
      />
      <Board articles={articles.slice(offset, offset + limit)} />
    </div>
  );
//Board.js
const Board = ({ articles }) => {
  return (
    <ul>
      {articles.map((article) => (
        <li key={article.id}>
          {article.id} | {article.title}
        </li>
      ))}
    </ul>
  );
};
//Pagination.js
const Pagination = ({ defaultPage, limit, total, onChange }) => {
  const [page, setPage] = useState(defaultPage);
  const totalPage = Math.ceil(total / limit);
  const handleChangePage = (newPage) => {
    onChange(newPage);
    setPage(newPage);
  };
  return (
    <div>
      <button onClick={() => page !== 0 && handleChangePage(page - 1)}>
        이전
      </button>
      {Array.from(new Array(totalPage), (_, i) => i).map((i) => (
        <button
          key={i}
          style={{ backgroundColor: page === i ? "red" : undefined }}
          onClick={() => handleChangePage(i)}
        >
          {i + 1}
        </button>
      ))}
      <button
        onClick={() => page + 1 !== totalPage && handleChangePage(page + 1)}
      >
        다음
      </button>
    </div>
  );
};

서버에서 내려준 건 아니고 한번에 내려준 값을 페이지네이션한 것이다. 보통 서버측에서 내려주긴하는데 클라이언트측에서도 하는 일이 있으니 알아두면 좋다 ㅎㅎ

profile
모르는 것을 모른다고 하기

0개의 댓글