# 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)
# 리액트의 생명주기 (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의 기능을 수정하면 모든 버튼에 변경 내요이 적용되기 때문에 유지보수가 훨씬 편리하다.
'코딩 알로하 :: one > react.js' 카테고리의 다른 글
리액트 To-do 리스트 만들기 (6탄) (0) | 2022.06.01 |
---|---|
리액트 To-do 리스트 만들기 (5탄) (0) | 2022.06.01 |
리액트 To-do 리스트 만들기 (4탄) (0) | 2022.06.01 |
리액트 To-do 리스트 만들기 (2탄) (0) | 2022.06.01 |
리액트 To-do 리스트 만들기 (1탄) (0) | 2022.06.01 |
댓글