[리액트] React Hook(useState, useEffect, useRef) 정리하기
리액트 숙련주차에 들어갔다. 입문 주차에서는 무엇인가 새로운 것을 배운다는 느낌에 집중도 잘되었는데 숙련 주차에 들어오니 끊임없는 정보때문에 머리가 헤롱하다. 과부하가 온 것 같다. 지금 배우고 있는 단계가 실무에서 가장 많이 쓰고 있는 것이라고 한다. 이럴때일수록 더 집중하고 이해해보려고 노력해야겠다.🔥
Styled-components
css-in-js 란?
자바스크립트 코드로 css 코드를 작성하여 컴포넌트를 꾸미는 방식이다. css를 사용할 때 조건문과 변수 등 다양한 로직을 적용해서 사용할 수 있다는 장점이 있다.
패키지 설치
yarn add styled-components
사용방법
태그의 속성명에 접근하여 스타일 주는 방법은 아래와 같다.
import styled from "styled-components";
styled.button`
// <button> HTML 엘리먼트에 대한 스타일 정의
`;
컴포넌트에 접근하여 스타일 주는 방법은 아래와 같다.
import styled from "styled-components";
import Button from "./Button";
styled(Button)`
// <Button> React 컴포넌트에 스타일 정의
`;
태그 속성에 원하는 스타일을 주고 StyledButton 변수에 저장한다. 해당 변수를 Button 컴포넌트에 사용하면 다른 리액트 컴포넌트에서 같은 스타일이 적용된 <Button>을 사용할 수 있다.
import React from "react";
import styled from "styled-components";
const StyledButton = styled.button`
padding: 6px 12px;
border-radius: 8px;
font-size: 1rem;
line-height: 1.5;
border: 1px solid lightgray;
color: gray;
background: white;
`;
function Button({ children }) {
return <StyledButton>{children}</StyledButton>;
}
import Button from "./Button";
<Button>Default Button</Button>;
useState()
기본 문법
컴포넌트의 state를 관리할 수 있는 기본적은 hook 이다. state를 변수로 사용하고, setState를 이용해서 state 값을 수정할 수 있다.
const [state, setState] = useState(initialState);
함수형 업데이트
카운트로 예시를 들어보자. 기존에 우리가 카운트 값을 증가시켰던 방법은 원시적 데이터로 값을 올려주었다. ( ) 안에 수정할 값이 아니라 함수를 넣어보자.
import { useState } from "react";
export default function useStatePage() {
const [count, setCount] = useState(0);
const onPlusHandler = () =>{
setCount(count + 1);
}
return (
<div>
<span> 카운트 {count}시 </span>
<button onClick={()=>onPlusHandler}>+</button>
</div>
);
}
( ) 안에 현재 number의 값을 가져와서 그 값에 +1을 더하여 반환하는 함수를 만들었다. 일반적인 업데이트 방식와 함수 업데이트 방식은 어떤 점이 다른지 아래에서 확인해보자.
setCount((currentNumber)=>{ return currentNumber + 1 });
일반 업데이트 방식은 버튼을 클릭했을 때 첫번째 줄 ~ 세번째 줄의 있는 setCount가 각각 실행되는 것이 아니라, 배치(batch)로 처리한다. 즉 우리가 onClick을 했을 때 setCount 라는 명령을 세번 내리지만, 리액트는 그 명령을 하나로 모아 최종적으로 한번만 실행을 시킨다는 뜻이다. 그래서 setCount을 3번 명령하던, 100번 명령하던 1번만 실행한다.
<button onClick={() => {
setCount(count + 1); // 첫번째 줄
setCount(count + 1); // 두번쨰 줄
setCount(count + 1); // 세번째 줄
}}
>
반면에 함수형 업데이트 방식은 3번을 동시에 명령을 내리면, 그 명령을 모아 순차적으로 각각 1번씩 실행시킨다. 현재값 0에 1더하고, 그 다음 1에 1을 더하고, 2에 1을 더해서 3이라는 결과가 나오는 것을 확인할 수 있다.
<button onClick={() => {
setCount((previousState) => previousState + 1);
setCount((previousState) => previousState + 1);
setCount((previousState) => previousState + 1);
}}
>
예시
자식 컴포넌트에 버튼을 눌렀을 때 부모 컴포넌트에 카운트가 증가하도록 하는 코드를 구현해보자.
import { useState } from 'react';
const App = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>여기는 부모컴포넌트입니다.</h1>
<span>현재 카운트 : {count}</span>
<Child setCount={setCount}/>
</div>
)
}
export default App;
Child 컴포넌트에 setCount만 props로 넘겨주면 된다. 함수형으로 값을 업데이트해줄 것이기때문에 count 까지 넘겨주어 setCount(count + 1) 을 해주는 것은 불필요한 props 전달이기때문이다.
<Child setCount={setCount}/>
const Child = (props) => {
const handleAddCound = () => {
setCount((prev) => prev + 1);
}
return <button oncClick={handleAddCound}>Count 1 증가</button>
}
export default Child;
useEffect()
기본 문법
리액트 컴포넌트가 렌더링 된 이후마다 특정 작업을 수행하도록 설정할 수 있는 Hook이다. 컴포넌트가 마운트 / 언마운트 / 업데이트 됐을 때 특정 작업을 처리할 수 있다.
useEffect(setup, dependencies?)
useEffect는 컴포넌트가 렌더링된 이후에 실행된다는 것이 핵심 포인트이다. 아래와 같은 코드는 input 태그를 만들어 state와 연결해준 상태이다. input창에 값을 입력했을 때 state가 변경하므로 리렌더링이 되면서 console.log 값이 계속해서 찍히는 것을 확인할 수 있다.
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect");
});
return (
<div>
<input
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value);
console.log("value => ", value);
}}
/>
</div>
);
}
export default App;
1. 인풋 값 입력 > 2. state 변경 > 3. 리렌더링 > 4. 렌더링이 되었으니 useEffect 다시 실행 > 반복 과 같은 흐름이다.
의존성 배열(defendency array)
Hook이 불필요하게 반복해서 실행되는 것을 방지하여 성능을 최적화하기 위해서 사용한다.
// useEffect의 두번째 인자가 의존성 배열이 들어가는 곳
useEffect(()=>{
// 실행하고 싶은 함수
}, [의존성배열]) //[] 이 배열에 값을 넣으면 그 값이 바뀔 때만 useEffect를 실행할게
✔️ Hook의 재실행 조건 설정
- 의존성 배열에 포함된 값들이 변경될 때만 훅이 재실행되도록 조건을 설정한다. 이를 통해 불필요한 실행을 방지할 수 있다.
✔️ 최신 상태 유지
- 의존성 배열을 사용하면 Hook이 항상 최신 상태의 값을 참조할 수 있다. 이를 통해 불필요한 연산을 방지할 수 있다.
✔️ 의존성 관리의 명확성
- 의존성 배열을 사용하면 훅이 어떤 값에 의존하고 있는지 명확하게 파악할 수 있다. 이는 코드의 가독성을 높이고, 의도하지 않은 의존성 관계를 방지할 수 있다.
의존성 배열이 비어있는 경우는, 렌더링된 이후 딱 한번만 실행하겠다는 뜻이다. 렌더링 이후 한번만 실행하고 싶다면 빈 배열을 넣어주면 된다.
// src/App.js
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect");
}, []); // 비어있는 의존성 배열
return (
<div>
<input
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value);
}}
/>
</div>
);
}
export default App;
반대로 의존성 배열이 채워져 있는 경우에는, 이 배열의 값이 변경될 때만 실행하겠다는 뜻이다. 인풋의 값이 바뀔 때마다 실행한다.
// src/App.js
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
useEffect(() => {
console.log("hello useEffect");
}, [value]); // value를 넣음
return (
<div>
<input
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value);
}}
/>
</div>
);
}
export default App;
클린 업(Clean up)
컴포넌트가 언마운트 즉, 사라졌을 때 실행하는 함수이다.
useEffect(()=>{
// 화면에 컴포넌트가 나타났을(mount) 때 실행하고자 하는 함수를 넣어주세요.
return ()=>{
// 화면에서 컴포넌트가 사라졌을(unmount) 때 실행하고자 하는 함수를 넣어주세요.
}
}, [])
useRef()
기본 문법
리렌더링과 상관없이 값을 기억하기 위해 사용된다. 이 특징을 이용해 자바스크립트 DOM API를 직접 사용하지 않고 DOM 요소를 다루기 위한 용도로도 자주 사용된다.
const 변수명 = useRef(초기값)
useRef()에 초기값을 주고 실행해보면 current : '초기값' 을 확인할 수 있다. 이는 current라는 키값을 지닌 프로퍼티가 생성되고, 값에 어떤 변경을 줄때도 이 current를 이용해서 한다는 점이다.
import "./App.css";
import { useRef } from "react";
function App() {
const ref = useRef("초기값");
console.log("ref 1", ref);
return <div></div>;
}
export default App;
아래 코드와 같이 ref.current 의 값을 바꾼 값으로 변경하고 실행해보면 다음과 같다.
import "./App.css";
import { useRef } from "react";
function App() {
const ref = useRef("초기값");
console.log("ref 1", ref);
ref.current = "바꾼 값";
console.log("ref 1", ref);
return <div></div>;
}
export default App;
이렇게 설정된 ref 값은 컴포넌트가 계속해서 렌더링 되어도 언마운트 전까지 값을 유지하며 currnet 속성은 값을 변경해도 상태를 변경할 때 처럼 리액트 컴포넌트가 리렌더링 되지 않는다는 특징이 있다.
저장공간
state와 같이 어떤 값을 저장하는 공간으로 사용한다. 대신 state는 리렌더링이 필요한 값을 다룰때 사용하고, ref는 변하지 않는 값을 다룰 때 사용하자. 변화는 감지하지만 그 변화가 렌더링을 발생시키지 않을 때! 사용하기!
Hook | rendering | change |
useState() | Y | 내부 변수 초기화 |
useRef() | N | 값 유지 |