Table of Contents
1. useState
2. useEffect
3. useReducer
4. useMemo
5. useCallback
6. useRef
7. Customized Hooks
8. Other Hooks
import React, { useState } from "react";
const Counter = () => {
const [value, setValue] = useState(0);
return (
<div>
<p>
현재 카운터 값은 <b>{value}</b>입니다
</p>
<button onClick={() => setValue(value + 1)}>+1</button>
<button onClick={() => setValue(value - 1)}>-1</button>
</div>
);
};
export default Counter;
function createBulkTodo(){
...}
(...)
const App = () => {
const [todos, setTodos] = useState(createBulkTodo);
(...)
}
⚠️ useState의 기본 값에
createBulkTodo()
를 넣었으면 리렌더링 될 때마다 createBulkTodo 함수가 호출되지만createBulkTodo
파라미터를 함수 형태로 넣어 주면 컴포넌트가 처음 렌더링 될 때만 createBulkTodo 함수가 호출된다.
useEffect(() => {
console.log("렌더링이 완료되었습니다!");
}, []);
[]
는 옵션. 생략 되었을 시 모든 state가 업데이트 될 때마다 실행된다.[]
안에 있는 state는 업데이트 될 때마다 실행; 그 밖에 있는 state 는 컴포넌트의 마운트/언마운트 때만 실행.의존 값이 있는 경우 예시
const [name, setName] = useState("");
const [nickname, setNickname] = useState("");
useEffect(() => {
console.log("렌더링이 완료되었습니다!");
}, [name]);
useEffect()
에서 cleanup 함수 반환[name]
대신 []
을 넣자.cleanup function:
- simply a function returned inside
useEffect
- used to prevent memory leaks and clean up side effects when a component unmounts or before running a new effect due to dependencies changing.
- 더보기: React의 useEffect에서 Cleanup(정리) 함수의 역할
useEffect(() => {
console.log("effect");
console.log(name);
return () => {
console.log("cleanup");
console.log(name);
};
}, [name]);
import Info from "./Info";
import { useState } from "react";
const App = () => {
const [visible, setVisible] = useState(false);
return (
<div>
<button
onClick={() => {
setVisible(!visible);
}}
>
{visible ? "숨기기" : "보이기"}
</button>
<hr />
{visible && <Info />}
</div>
);
};
export default App;
- useState보다 더 다양한 컴포넌트 상황에 따라 다양한 상태를 다른 값으로 업데이트해 주고 싶을 때 사용하는 Hook이다.
- 현재 상태, 그리고 업데이트를 위해 필요한 정보를 담은 action값을 전달받아 새로운 상태를 반환하는 함수.
- ⚠️ 리듀서 함수에서 새로운 상태를 만들 때는 반드시 불변성을 지켜주어야 한다.
- 가장 큰 장점은 컴포넌트 업데이트 로직을 컴포넌트 바깥으로 빼낼 수 있다는 것
import React, { useReducer } from "react";
function reducer(state, action) {
return {
...state,
[action.name]: action.value,
};
}
const Info = () => {
const [state, dispatch] = useReducer(reducer, {
name: "",
nickname: "",
});
const { name, nickname } = state;
const onChange = (e) => {
dispatch(e.target);
};
return (
<div>
<div>
<input name="name" value={name} onChange={onChange} />
<input name="nickname" value={nickname} onChange={onChange} />
</div>
<div>
<div>
<b>Name: </b> {name}
</div>
<div>
<b>Nickname:</b> {nickname}
</div>
</div>
</div>
);
};
export default Info;
import React, { useState, useMemo, useRef, useCallback } from 'react';
const getAverage = numbers => {
console.log('평균값 계산중..');
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState('');
const inputEl = useRef(null);
const onChange = useCallback(e => {
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
inputEl.current.focus();
}, [number, list]); // number 혹은 list 가 바뀌었을 때만 함수 생성
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange} ref={inputEl} />
<button onClick={onInsert}>등록</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>평균값:</b> {avg}
</div>
</div>
);
};
export default Average;
// Before
(useMemo가 없음)
(...)
<div>
<b>평균값:</b> {getAverage(list)}
</div>
해당 코드에서는 input value 가 onchange 일 때는 평균값을 다시 계산할 필요가 없는데 숫자를 등록할 때뿐만 아니라 인풋 내용이 수정될 때도 (즉 리렌더링 될 때마다) 우리가 만든 getAverage함수가 호출되는 것을 확인할 수 있다.
useMemo는 렌더링하는 과정에서 특정 값이 바뀌었을 때만 연산을 실행하고, 원하는 값이 바뀌지 않았다면 이전에 연산했던 결과를 다시 사용한다.
// After
const avg = useMemo(() => getAverage(list), [list]);
(...)
<div>
<b>평균값:</b> {avg}
</div>
- useMemo 에 있는 예제 참고
// Before
const onChange = e => {
setNumber(e.target.value);
};
const onInsert = e => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
};
👎 The code above forces components to use newly created function whenever rerendering happens.
// After
const onChange = useCallback(e => {
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링 될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
inputEl.current.focus();
}, [number, list]); // number 혹은 list 가 바뀌었을 때만 함수 생성
props로 전달해야 할 함수를 만들 때는 useCallback을 사용하여 함수를 감싸는 것을 습관화 (컴포넌트 성능 이슈)
If component renders often or there are many components that has to be rendered, useuseCallback
for optimization.
차이 | UseMemo | UseCallback |
---|---|---|
메모이제이션 대상 | 계산 결과 값 | 함수자체 |
사용목적 | 비용이 높은 계산 결과를 재사용 | 함수의 참조 일관성 유지, 불필요한 리렌더링 방지 |
사용 시나리오 | 복잡한 계산 | 자식 컴포넌트에 함수를 props를 전달하는 경우, 이벤트 핸들러가 자주 변경 되지 않아야 하는 경우 |
- useMemo 에 있는 예제 참고
const inputEl = useRef(null);
(...)
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
inputEl.current.focus();
}, [number, list]);
(...)
<input value={number} onChange={onChange} ref={inputEl} />
// usePromise.js
import { useState, useEffect } from "react";
export default function usePromise(promiseCreator, deps) {
// promiseCreater = 프로미스를 사용하는 (감싸는) 함수
// deps = usePromise 내부에서 사용한 useEffect의 의존배열
// 로딩중 / 완료 / 실패에 대한 상태 관리
const [loading, setLoading] = useState(false);
const [resolved, setResolved] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const process = async () => {
setLoading(true);
try {
const resolved = await promiseCreator();
setResolved(resolved);
} catch (e) {
setError(e);
}
setLoading(false);
};
process();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return [loading, resolved, error];
}
본 후기는 [한글과컴퓨터x한국생산성본부x스나이퍼팩토리] 한컴 AI 아카데미 (B-log) 리뷰로 작성 되었습니다.
#한컴AI아카데미 #AI개발자 #AI개발자교육 #한글과컴퓨터 #한국생산성본부 #스나이퍼팩토리 #부트캠프 #AI전문가양성 #개발자교육 #개발자취업