데브코스 41일차 TIL : React [리액트 시작, JSX, 컴포넌트의 상태관리 및 이벤트바인딩, useEffect, useRef, craco]

te-ing·2021년 10월 12일
0
post-thumbnail

드디어 리액트 입문! 과 동시에 자신감 하락. Vue가 왜 더 쉽다고 하는지를 뼈저리게 느끼는 중. 하지만 결국 흐름은 명확하게 있는 듯하니, 공부하다보면 사랑스러워 지지 않을까 싶다. 오래보아야 사랑스럽다. 리액트도 그렇다. 제발 그래야만 한다.

create-react-app

웹팩 등의 설정 없이 간편하게 리액트를 사용할 수 있는 방법

npx create-react-app tj-app , npm start

JSX

리액트에서는 JSX 사용를 사용하는데 JSX를 사용할 때는

class를 ⇒ className : 이미 예약된 class 예약어가 있기 때문

최상위요소가 하나여야 한다. 다른 DOM으로 감싸거나 React.Fragments를 이용해야 한다.

리액트(JSX)의 표현식

데이터 사용

{ } 을 이용해 데이터사용

function App() { const name = '리액트' return ( <h1\>{ name }</h1>) }

조건식

삼항연산자, && 등을 사용

{ showLink && (<h1\>{ name }</h1>) }: showLink 가 true일 때 h1태그 동작

반복문

key를 넣어줘야 최적화 가능

<ul>{ name.map((item) ⇒ ( <li key={item}> { item } </li>))}</ul>

props와 defaultProps, propTypes, children

// App.js
import './App.css';
import Logo from './components/Logo';
import Paragraph from './components/Paragraph';

<Logo size={100} /> // string 사용시 "100px", Number 사용시 { }
<Logo /> // 기본값 200 사용됨

<Paragraph>
  Edit <code>src/App.js</code> and save to reload.
</Paragraph>
// Logo/index.js
import PropTypes from 'prop-types';

function Logo(props) {
  return(
    <img src={logo} className="App-logo" alt="logo" 
    style={{ width: props.size, height: props.size }} />
  )
}
Logo.defaultProps = { // 기본값 정의
  size: 200,
}
Logo.propTypes = {
  size: PropTypes.number
}
export default Logo;
// Paragraph/index.js
import PropTypes from 'prop-types';

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

Paragraph.propTypes = {
  children: PropTypes.node.isRequired
}

export default Paragraph;

function Logo({ size = 200 }) 과 같이 비구조화 할당 사용가능

defaultProps: 기본값 정의 , propTypes: 데이터타입 제한

{ children }: 하위요소 그대로 사용

children: PropTypes.node.isRequired: 여기서 node는 jsx같은 element를 받을 수 있는 타입, isRequired 필수로 들어가야 할 요소

컴포넌트의 상태관리 및 이벤트바인딩

지역상태 관리 및 이벤트바인딩

import { useState } from "react"

function Counter() {
  const handleIncrease = () => {
    setCount(count + 1);
  }
  return(
    <div>
      { count }
      <button onClick={handleIncrease}>+</button>
    </div>
  )
}
export default Counter

// App.js
import Counter from './components/Counter'

function App() {
  const [totalCount, setTotalCount] = useState(0);

  return (
    <div>
      <Counter />
    </div>

const [count, setCount] = useState(0); 여기서 count는 상태, setCoun는: 값을 바꿔주는 함수, useState(0); 훅, 함수 내의 상태를 관리할 수 있음

부모에게 메시지 전달

import { useState } from "react"
import ProtoTypes from 'prop-types'

function Counter({ onIncrease, onDecrease }) {
  const [count, setCount] = useState(0)

  const handleIncrease = () => {
    setCount(count + 1)
    if (onIncrease) onIncrease(count + 1) // 검증
  }

  const handleDecrease = () => {
    setCount(count - 1)
    if (onDecrease) onDecrease(count - 1)
  }

  return(
    <div>
      { count }
      <button onClick={handleIncrease}>+</button>
      <button onClick={handleDecrease}>-</button>
    </div>
  )
}

Counter.protoTypes = {
  onDecrease: ProtoTypes.func, // 함수
  onIncrease: ProtoTypes.func 
}

export default Counter

//App.js
import { useState } from 'react'
import Counter from './components/Counter'

function App() {
  const [totalCount, setTotalCount] = useState(0)

  return (
    <div>
      totalCount: {totalCount}
      <Counter 
        onIncrease={(count) => { setTotalCount(totalCount + 1) }}
        onDecrease={(count) => { setTotalCount(totalCount - 1) }} />
        <Counter 
       onIncrease={(count) => { setTotalCount(totalCount + 1) }}
        onDecrease={(count) => { setTotalCount(totalCount - 1) }} />
    </div>
  );
}

export default App;

useEffect

변화를 감지하여 반응하는 Hook

useEffect(() => {
    console.log(`Clicked ${count} times.`)
  }, [count]) 

count가 변할 때 마다 console.log 사용됨

count에 아무 값을 넣지 않으면 라이프사이클 시작 시 실행됨

useEffect(() => {
    console.log('Component Loaded')
    const handleScroll = () => {
      console.log(window.scrollY);
    };
    document.addEventListener("scroll", handleScroll)
    return () => document.removeEventListener("scroll", handleScroll) 
  }, [])

전역 이벤트 사용: document.addEventListener("scroll", handleScroll)

전역 이벤트 사용시 return을 통해 컴포넌트가 끝날 때 이벤트삭제 필수적으로 넣어야 함!

useRef

DOM에 직접 접근할 때나 지역변수로 사용할 때 사용한다.

값이 변경될 때마다 렌더링하는 useState와는 달리 값이 변경되더라도 다시 렌더링하지 않는다.

DOM에 직접 접근

// App.js
import { useRef } from 'react'
import Input from "./components/Input.js"

function App() {
  const inputRef = useRef();
  return (
    <div>
      <Input ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus</button>
    </div>
  );
}
export default App;

// Input.js
import React from "react"

const Input = React.forwardRef((_, inputRef) => {
  return (
    <>
    <input ref={inputRef} />
    </>
  )
});
export default Input;

inputRef=useRefInput ref={inputRef} 을 이용하여 DOM에 접근하여 Input과 input연결

use Ref의 지역변수로 사용

import { useRef, useState } from "react"

const AutoCounter = () => {
  const [count, setCount] = useState(0)
  const intervalId = useRef() 
  const handleStart = () => {
    intervalId.current = setInterval(()=>{
      setCount((count) => count + 1)
    }, 1000);
  }
  
  const handleStop = () => {
    clearInterval(intervalId.current)
  }

  return(
    <>
      <div>{count}</div>
      <button onClick={handleStart}>Start</button>
      <button onClick={handleStop}>Stop</button>
    </>
  )
}
export default AutoCounter;

// App.js
import AutoCounter from './components/AutoCounter.js';

function App() {
  return (
      <AutoCounter/>
  );
}

export default App;

useRef로 intervarId를 넣어주기 때문에 값이 변하더라도 다시 렌더링하지 않음

craco 라이브러리 이용

/* @jsxImportSource @emotion/react / 없이 사용가능

craco(create-react-app-config-override) 라이브러리 npm i -D @craco/craco

craco.config.js 파일생성 후 @emotion/babel-preset-css-prop 설치

// craco.config.js
module.exports = {
  babel: {
    "presets": ["@emotion/babel-preset-css-prop"]
  }
}

packge.json에서 "start": "react-scripts start""start": "craco start" 수정

"build": "craco build", "test": "craco test"

styled 를 이용한 css 적용

import styled from "@emotion/styled";

const Box = styled.div`
  width: 100px; 
  height: 100px;
  background-color: aqua;
`;

export default Box;

// App.js
function App() {
  return (
    <div>
      <div css= {{ width: 200, height: 100, backgroundColor: "black" }} />
      <Box />
    </div>
  );
}

<div css= {{ width: 200, height: 100, backgroundColor: "black" }} /> 처럼 인라인 형식으로도 사용가능

profile
병아리 프론트엔드 개발자🐣

0개의 댓글