본문 바로가기
  • 인공지능
  • 블록체인
  • 정보보안
코딩 알로하 :: one/react.js

리액트 To-do 리스트 만들기 (3탄)

by nathan03 2022. 6. 1.
반응형

# React State와 useState 
지금까지는 페이지를 렌더링할때 정적인(Static)요소들만 사용했다. 그런데 웹에서는 사용자와의 상호작용을 통해 화면이 바뀌는 경우가 많다. 이럴때 useState 를 사용하면 "상태 관리"가 가능하다. 

< +1, -1 버튼을 클릭하면 화면의 숫자가 변하는 예제 > 
return 부분을 자세히 보면 JSX <button>에 onClick={onIncrease} 와 같은 애트리뷰트를 볼수 있다. onIncrease는 counter컴포넌트에 정의된 함수이다. 이 함수가 하는 일은 useState의 결과 값인 number의 값에 1을 더하는 것이다. 
그럼 useState가 하는 일이 뭘까? useState는 기본값을 입력으로 받아 배열을 리턴하는데, 첫 번째 원소는 현재 상태를 나타내는 변수, 두번째는 이 상태를 변경할 수 있는 Setter함수이다. onIncrease함수에서 이 setNumber 가 바로 number의 상태를 변경할 수 있는 Setter 함수이다. 

// App.js
import React, { useState } from "react";

function Counter() {
  const [number, setNumber] = useState(0);

  const onIncrease = () => {
    setNumber(number + 1);
  };

  const onDecrease = () => {
    setNumber(number - 1);
  };

  return (
    <div>
      <h1>{number}</h1>
      <button onClick={onIncrease}>+1</button>
      <button onClick={onDecrease}>-1</button>
    </div>
  );
}

export default Counter;

 

# 수학 점수 표기 

//App.js

import React from 'react';
import Input from './Input';

function App(){
  return <Input/>
}
export default App;
//Input.js

import React, { useState } from "react";

function Input() {
    const [inputs, setInputs] = useState({
        subject: "",
        score: "",
    });

    const onChange = (event) => {
        const { value, name } = event.target;
        setInputs({
            ...inputs,
            [name]: value
        });
    };

    return (<>
        <div>
            <h2>성적:{inputs.subject}{inputs.score}</h2>
            <input name="subject" placeholder="수학" value={inputs.subject} onChange={onChange}/>
            <input name="score" placeholder="99" value={inputs.score} onChange={onChange}/>
        </div>
    </>
    );
}

export default Input;

 

# 과학 점수 표기 (초기화 버튼 추가)
서버를 실행시키면 다음과 같은 화면이 나온다. 입력창에 텍스트를 입력하면 화면의 Value: 에 해당하는 텍스트가 바뀌게 되고, Initialize버튼을 누르면 입력창과 텍스트가 빈 텍스트로 대체된다. 

import React, { useState } from "react";

function Input() {
  const [inputs, setInputs] = useState({
    subject: "",
    score: "",
  });

  const onChange = (event) => {
    const { value, name } = event.target;
    setInputs({
      ...inputs,
      [name]: value,
    });
  };

  const onReset = () => {
    setInputs({
      subject: "",
      score: "",
    });
  };

  return (
    <>
      <div>
        <h2>
          성적:{inputs.subject}
          {inputs.score}
        </h2>
        <input
          name="subject"
          placeholder="수학"
          value={inputs.subject}
          onChange={onChange}
        />
        <input
          name="score"
          placeholder="99"
          value={inputs.score}
          onChange={onChange}
        />
        <button onClick={onReset}>Init</button>
      </div>
    </>
  );
}

export default Input;

 

# 함수형 컴포넌트에서 state 를 사용하기 위한 방법 (React Hook) 
지금까지 스테이트 관리를 위해 useState를 사용했는데, 사실 이 함수는 리액트 훅이다. 훅이란 무엇일까? 이름이 어렵지만, 사실은 기존에 사용하던 클래스 컴포넌트의 여러 기능들을 자연스럽게 함수형 컴포넌트에서 사용하기 위한 장치이다. 

기존의 클래스형 컴포넌트는 스테이트를 직접 다룰수 있을뿐만 아니라 컴포넌트의 생명주기, 즉 라이플사이클도 직접적으로 다룰수 있었다. 클래스형 컴포넌트는 자바스크립트 클래스로 구성되어있기 때문에 기본적인 자바스크립트 문법을 잘 알아야 하는 진입장벽이 존재 했다. 게다가 간결하지 못한 문법 때문에 코드를 알아보기도 힘들었다. 

하지만 함수형 컴포넌트를 사용하면 쉽고 간결하게 컴포넌트를 만들 수 있지만, 스테이트와 생명주기 관리가 어려워지는 문제가 있었다. 리액트 16.8 버전 이후부터는 이 문제를 리액트 훅을 추가해 해결했다. 즉 함수형 컴포넌트가 기존의 클래스형 컴포넌트를 완전히 대체할 수 있도록 하는 기능이다. 

Hook의 개요 – React (reactjs.org)

 

Hook의 개요 – React

A JavaScript library for building user interfaces

ko.reactjs.org

# 리액트의 생명주기 (Life Cycle)
리액트는 컴포넌트가 화면에 렌더링되는 과정을 생명주기를 이용해 관리한다. 실제로는 이 생명주기가 굉장히 복잡하게 구성되어 있지만, 간단하게 정리하면 다음과 같이 정리할 수 있다. 

먼저 생명주기는 크게 3개의 페이즈(마운팅, 업데이팅, 언마운팅)로 구성된다. 
  - 마운팅 : DOM에 컴포넌트가 등록되는 과정 
  - 업데이팅 : 등록된 컴포넌트의 내용이 업데이트 되는 과정 
  - 언마운팅 : DOM에서 사라지는 과정 

마운팅 페이즈에서 우리가 만든 컴포넌트가 DOM에 등록되기 전에 렌더링을 거치고, 렌더링 과정에서 업데이팅 진행되어 props나 스테이트의 내용이 컴포넌트에 업데이트 된다. 그 다음 실제 브라우저의 DOM에 컴포넌트가 등록된다. 그러다 더이상 컴포넌트가 사용되지 않게 되면 언마운팅되게 된다. 

리액트의 모든 컴포넌트는 이러한 생명주기를 가지고 있다. 이 생명주기를 알아야 하는 이유는, 렌더링이 끝나기 전, 즉 브라우저 DOM에 업데이트 되기 전까지는 리액트에서 관리 하는 영역이지만, 그 이후에는 브라우저 그리고 화면에 나타나는 과정이기 때문이다. 

# useEffect

컴포넌트가 마운트되어 DOM이 변경된 다음에 실행된다. 즉 컴포넌트 마운트(DOM 변경) -> 렌더링 -> useEffect 실행의 순서로 이루어진다.  useEffect는 보통 다음과 같은 작업을 수행한다. 

    가. props에 속한 값을 컴포넌트의 로컬 변수로 선언 
    나. 외부 API 호출을 통해 값을 받아와서 State에 넣어줄 때 사용 
    다. 서드파티 라이브러리 사용 

//App.js

import React, { useState, useEffect } from 'react';

function App() {
  const [count, setCount] = useState(0);


  useEffect(
    () => {
      console.log("useEffect");
      document.title = 'Youe clicked ${count} times';
    });

  return (
    <>
      <p>You clicked {count} times</p>
      <button onClick={() => {
        console.log("Click"); 
        setCount(count+1);
      }} 
      >
        Click Me
      </button>
      </>
  );
}


export default App;

# useNavigate

npm install react-router-dom // 설치
//App.js

import React from "react";
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  useNavigate,
} from "react-router-dom";

export default function BasicExample() {
  return (
    <BrowserRouter>
      <button>
        <Link to="/home">Home</Link>
      </button>
      <button>
        <Link to="/">Index</Link>
      </button>
      <Routes>
        <Route exact path="/" element={<Index />} />
        <Route path="/home" element={<Home />} />
      </Routes>
    </BrowserRouter>
  );
}

function Index() {
  let navigate = useNavigate();
  function handleClick() {
    navigate("/home");
  }
  return (
    <>
      <h2>Index</h2>
      <button type="button" onClick={handleClick}>
        Go home
      </button>
    </>
  );
}

function Home() {
  return (
    <>
      <h2>Home</h2>
      <p>This is home</p>
    </>
  );
}

 

# UseRef
자바스크립트에서는 특정 DOM을 선택할 때 다음과 같이 하면 된다.

document.getElementById("#foo.bar")
document.querySelector('#foo.bar')

리액트에서 비슷한 작업을 해야하는 경우 useRef 함수를 사용해 다음과 같이 할 수 있다. 아래 예제에서는 특정 함수가 호출되는 경우, 포커스를 해당 엘리먼트에 맞추고 있다. 

//Input.js

import { useState, useRef } from "react";

export default function App() {
  const [text, setText] = useState("");
  const onChange = (event) => {
    const target = event.target;
    setText(target[1]);
  };
  const newInput = useRef();
  const onReset = () => {
    setText("");
    newInput.current.focus();
  };
  return (
    <div>
      <input ref={newInput} onChange={onChange} value={text}></input>{" "}
      <button onClick={onReset}>Reset</button>
    </div>
  );
}

 

# Custom hook 

Custom hook 이란, 리액트에 정의되어 있지 않은 나만의 Hook을 만들어 사용하는 것을 의미 한다. 

//App.js

import { useState } from "react";

function App() {
  const [click1, setState1] = useState(false);
  const toggle1 = () => setState1((click1) => !click1);
  const [click2, setState2] = useState(false);
  const toggle2 = () => setState2((click2) => !click2);
  const [click3, setState3] = useState(false);
  const toggle3 = () => setState3((click3) => !click3);
  const [click4, setState4] = useState(false);
  const toggle4 = () => setState4((click4) => !click4);
  const [click5, setState5] = useState(false);
  const toggle5 = () => setState5((click5) => !click5);
  return (
    <>
      <button onClick={toggle1}>{click1 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={toggle2}>{click2 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={toggle3}>{click3 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={toggle4}>{click4 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={toggle5}>{click5 ? "Hello" : "Goodbye"}</button>
    </>
  );
}
export default App;

버튼을 클릭하면, state가 반전되는 역할을 수행하는 setter 함수를 사용하고 있다. 문제는 이 패턴이 5개의 버튼에 동일하게 반복된다는 점이다. 물론 이렇게 간단한 경우에는 단순히 반복해서 코드를 작성해도 된다. 하지만 코드가 복잡해지고, 훨씬 많은 곳에서 이 코드를 사용한다면 이 기능을 별도의 함수, 즉 Hook 으로 만들어서 관리하는 것이 훨씬 좋은 방법이다.  위의 내용을 custom hook으로 만들어 적용해보면 아래와 같다. useToggle 이라는 커스텀훅을 만들시 명명법(Hook 명명법)은 두번째 명사에 대문자로 표기해야 한다. 

//App.js

import { useState } from "react";


function App() {
  
  const [click1, setClick1] = useToggle();
  const [click2, setClick2] = useToggle();
  const [click3, setClick3] = useToggle();
  const [click4, setClick4] = useToggle();
  const [click5, setClick5] = useToggle();
  return (
    <>
      <button onClick={setClick1}>{click1 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={setClick2}>{click2 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={setClick3}>{click3 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={setClick4}>{click4 ? "Hello" : "Goodbye"}</button>{" "}
      <button onClick={setClick5}>{click5 ? "Hello" : "Goodbye"}</button>
    </>
  );
}
const useToggle = (initialState = false) => {
  const [state, setState] = useState(initialState);
  const toggle = () => setState((state) => !state);
  return [state, toggle];
};

export default App;

useToggle이라는 Hook 덕분에 코드가 훨씬 직관적이고 간결해졌다. 게다가 Hook의 기능을 수정하면 모든 버튼에 변경 내요이 적용되기 때문에 유지보수가 훨씬 편리하다. 

반응형

댓글