1. Hooks란 무엇인가?
Hooks는 리엑트 v16.8에 새로 도입된 기능으로 기존의 함수
컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 도와줍니다.
2. useState
함수 컴포넌트에서 상태 관리를 위한 훅입니다
하나의 상태 값만 관리할 수 있으므로 관리해야 할 상태가
여러개라면 useState를 여러번 사용하면 됩니다.
import {useState} from 'react'
const [value, setValue] = useState(0)
// value: 값
// setValue: 값의 상태를 변경하고 리렌더링됩니다.
// useState(): 괄호 안에 기본 값을 넣어줍니다.
3. useEffect
리엑트 컴포넌트가 렌더링될 때마다 작업을 생명 주기(Lifecycle)와
의존성 배열에 따라서 수행하도록 설정할 수 있는 훅입니다.
import {useEffect} from 'react'
const useEffect(( ) => {
//Mounting
return(
//unMounting
)
}, [
//Dependency array
])
4. useReducer
useState보다 더 다양한 컴포넌트 상황에 따라
값을 업데이트해 주고 싶을 때 사용하는 훅입니다.
import {useReducer} from 'react'
function reducer(state, action) {
return {
// 불변성을 유지해줘야 합니다
...state,
[action.name]: action.value
}
}
// state와 dispatch 함수를 받아옵니다.
const [state, dispatch] = useReducer(reducer, {value: 0});
// action에 type 필요없이 어떤 값도 들어올 수 있습니다.
const onChange = e => {
dispatch(e.target)
}
// state: 현재 상태
// dispatch: 액션을 발생시키는 함수
// reducer: 리듀서 함수
// {value: 0}: 기본 값
5. useMemo
함수 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있습니다.
렌더링 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고,
원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용 합니다.
import {useMemo} from 'react'
const getDiaryAnalysis = useMemo(() => {
console.log('useMemo 연산 시작함');
const sum = x + y;
return { sum };
}, [x, y]);
// {}: callback Function
// []: Dependency array(의존성 배열)
6. useCallback
useMemo와 유사한 함수입니다.
주로 렌더링 성능을 최적화해야 하는 상황에서 사용합니다.
만들어 놨던 함수를 재사용할 수 있습니다.
import {useCallback} from 'react'
const getDiaryAnalysis = useCallback(() => {
const nextList = list.concat(parseint(number));
setList(nextList)
setNumber('')
}, [number, list]);
{}: callback Function
[]: Dependency array(의존성 배열)
7. useRef
함수 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해 줍니다.
useRef 사용하여 포커싱 주기
import { useEffect, useRef } from "react";
import "./App.css";
function App() {
const idRef = useRef("");
// 렌더링이 될 때
useEffect(() => {
idRef.current.focus();
}, []);
return (
<>
<div>
아이디 : <input type="text" ref={idRef} />
</div>
<div>
비밀번호 : <input type="password" />
</div>
</>
);
}
export default App;
// 아이디가 10자리 입력되면 자동으로 비밀번호 필드로 이동하도록.
// `id.length ≥ 10` 이 로직을 useEffect 안에 넣었는지 곰곰히 생각!
(힌트 : 배치 업데이트)
import { useEffect, useRef, useState } from "react";
import "./App.css";
function App() {
const idRef = useRef("");
const pwRef = useRef("");
const [id, setId] = useState("");
const onIdChangeHandler = (event) => {
setId(event.target.value);
};
// 렌더링이 될 때
useEffect(() => {
idRef.current.focus();
}, []);
// 왜 useEffect 안에 놓았을까요?
useEffect(() => {
if (id.length >= 10) {
pwRef.current.focus();
}
}, [id]);
return (
<>
<div>
아이디 :
<input
type="text"
ref={idRef}
value={id}
onChange={onIdChangeHandler}
/>
</div>
<div>
비밀번호 : <input type="password" ref={pwRef} />
</div>
</>
);
}
export default App;
import {useRef, useEffect} from 'react'
// 1. ref는 값을 저장하다가 리렌더링 되면 한꺼번에 보여준다.
const countRef = useRef(0)
const countHandler = () => countRef.current++
// 2. DOM요소를 선택할 수 있다.
const countRef2 = useRef("")
const useEffect(() => {
countRef2.current.focus()
}, [])
return (
<>
<button onClick = {countHandler}> ref 증가</button>
<input type = "text" ref = {countRef2}/>
</>
)
8. useContext
// #1. context > FamilyContext.js 생성
import { createContext } from "react";
// 여기서 null이 의미하는 것은 무엇일까요?
export const FamilyContext = createContext(null);
//#2. GrandFather.jsx 수정
import React from "react";
import Father from "./Father";
import { FamilyContext } from "../context/FamilyContext";
function GrandFather() {
const houseName = "스파르타";
const pocketMoney = 10000;
return (
<FamilyContext.Provider value={{ houseName, pocketMoney }}>
<Father />
</FamilyContext.Provider>
);
}
export default GrandFather;
#3. Father.jsx 수정(props를 제거해요!)
import React from "react";
import Child from "./Child";
function Father() {
return <Child />;
}
export default Father;
#4. Child.jsx 수정
import React, { useContext } from "react";
import { FamilyContext } from "../context/FamilyContext";
function Child({ houseName, pocketMoney }) {
const stressedWord = {
color: "red",
fontWeight: "900",
};
const data = useContext(FamilyContext);
console.log("data", data);
return (
<div>
나는 이 집안의 막내에요.
<br />
할아버지가 우리 집 이름은 <span style={stressedWord}>{data.houseName}</span>
라고 하셨어요.
<br />
게다가 용돈도 <span style={stressedWord}>{data.pocketMoney}</span>원만큼이나
주셨답니다.
</div>
);
}
export default Child;
// GrandFather → Context(중앙 관리소) → Child 순서로 전달!
object를 이용.
<span style={stressedWord}>{data.houseName}</span>
<span style={stressedWord}>{data.pocketMoney}</span>
## 주의해야 할 사항
- 렌더링 문제
useContext를 사용할 때, Provider에서 제공한 value가 달라진다면 useContext를 사용하고 있는 모든 컴포넌트가 리렌더링 됩니다. 따라서 value 부분을 항상 신경써줘야 해요!
이후에 배우게 될 메모이제이션이 그 키가 되겠네요 🙂
9. React Hooks - 최적화(React.memo, useCallback, useMemo)
// App.jsx
import React, { useState } from "react";
import Box1 from "./components/Box1";
import Box2 from "./components/Box2";
import Box3 from "./components/Box3";
const boxesStyle = {
display: "flex",
marginTop: "10px",
};
function App() {
console.log("App 컴포넌트가 렌더링되었습니다!");
const [count, setCount] = useState(0);
// 1을 증가시키는 함수
const onPlusButtonClickHandler = () => {
setCount(count + 1);
};
// 1을 감소시키는 함수
const onMinusButtonClickHandler = () => {
setCount(count - 1);
};
return (
<>
<h3>카운트 예제입니다!</h3>
<p>현재 카운트 : {count}</p>
<button onClick={onPlusButtonClickHandler}>+</button>
<button onClick={onMinusButtonClickHandler}>-</button>
<div style={boxesStyle}>
<Box1 />
<Box2 />
<Box3 />
</div>
</>
);
}
export default App;
Box1.jsx
import React from "react";
const boxStyle = {
width: "100px",
height: "100px",
backgroundColor: "#91c49f",
color: "white",
// 가운데 정렬 3종세트
display: "flex",
justifyContent: "center",
alignItems: "center",
};
function Box1() {
console.log("Box1이 렌더링되었습니다.");
return <div style={boxStyle}>Box1</div>;
}
export default Box1;
memo를 통해 해결해보기
우리는 정말 간단히 React.memo를 이용해서 컴포넌트를 메모리에 저장해두고 필요할 때 갖다 쓰게 됩니다. 이렇게 하면 부모 컴포넌트의 state의 변경으로 인해 props가 변경이 일어나지 않는 한 컴포넌트는 리렌더링 되지 않아요. 이것을 컴포넌트 memoization 이라고 합니다.
한번 해볼게요!
Box1.jsx, Box2.jsx, Box3.jsx 모두 동일
export default React.memo(Box1);
export default React.memo(Box2);
export default React.memo(Box3);
10. useCallback
App.jsx
...
// count를 초기화해주는 함수
const initCount = useCallback(() => {
console.log(`[COUNT 변경] ${count}에서 0으로 변경되었습니다.`);
setCount(0);
}, [count]);
...
11. useMemo
App.jsx
import "./App.css";
import HeavyComponent from "./components/HeavyComponent";
function App() {
const navStyleObj = {
backgroundColor: "yellow",
marginBottom: "30px",
};
const footerStyleObj = {
backgroundColor: "green",
marginTop: "30px",
};
return (
<>
<nav style={navStyleObj}>네비게이션 바</nav>
<HeavyComponent />
<footer style={footerStyleObj}>푸터 영역이에요</footer>
</>
);
}
export default App;
components > HeavyComponent.jsx
import React, { useState, useMemo } from "react";
function HeavyButton() {
const [count, setCount] = useState(0);
const heavyWork = () => {
for (let i = 0; i < 1000000000; i++) {}
return 100;
};
// CASE 1 : useMemo를 사용하지 않았을 때
const value = heavyWork();
// CASE 2 : useMemo를 사용했을 때
// const value = useMemo(() => heavyWork(), []);
return (
<>
<p>나는 {value}을 가져오는 엄청 무거운 작업을 하는 컴포넌트야!</p>
<button
onClick={() => {
setCount(count + 1);
}}
>
누르면 아래 count가 올라가요!
</button>
<br />
{count}
</>
);
}
export default HeavyButton;
useMemo를 활용할 수 있어요!
const me = useMemo(() => {
return {
name: "Ted Chang",
age: 21,
isAlive: isAlive ? "생존" : "사망",
};
}, [isAlive]);
- **주의해야 할 사항**
useMemo를 남발하게 되면 별도의 메모리 확보를 너무나 많이 하게 되기 때문에 오히려 성능이 악화될 수 있습니다. 필요할 때만 쓰기로 합시다 🙂
✅커스텀 Hook 만들기
비슷한 기능을 공유할 경우, 커스텀 Hook으로 작성하여
컴포넌트를 깔끔하게 분리해서 재사용할 수 있습니다.
'React' 카테고리의 다른 글
아이디&비밀번호 입력 alert&초기화 (0) | 2024.01.09 |
---|---|
로그인&회원가입&로그아웃 기능 구현(lv.4) (1) | 2024.01.09 |
컴포넌트 반복 (1) | 2024.01.07 |
컴포넌트 스타일링 (0) | 2024.01.06 |
Thunk 기본 세팅_setTimeout 3초(+ / -) (0) | 2024.01.06 |