안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.
Hooks - useCallback, useRef, 커스텀 Hooks 만들기
useCallback
- 렌더링 성능을 최적화할 때 사용
- 함수 재사용
Average.js
import { useState, useMemo } from "react";
const getAverage = numbers => {
console.log('평균값 계산 중...');
if(numbers.length === 0) return 0;
const sum = numbers.reduce((agg, num) => agg + num);
return sum / numbers.length;
}
const Average = () => {
const [ list, setList ] = useState([]);
const [ number, setNumber ] = useState('');
const onChange = e => {
setNumber(e.target.value);
}
const onInsert = () => {
const nextList = list.concat(parseInt(Number(number)));
setList(nextList);
setNumber('');
}
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>등록</button>
<ul>
{
list.map((value, index) => (
<li key={index}>{value}</li>
))
}
</ul>
<div>
<b>평균값 : </b> {avg}
</div>
</div>
)
}
export default Average;
현재 onChange와 onInsert 함수를 선언해 주었습니다.
하지만 이렇게 선언하면 컴포넌트가 리렌더링될 때마다 새로 만들어진 함수를 사용하게 됩니다.
추후 렌더링이 자주 발생하거나 컴포넌트 개수가 많아진다면 useCallback을 이용하여 최적화 해주는 것이 좋습니다.
Average.js - onChange, onInsert
const onChange = useCallback(e => {
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링될 때만 함수 생성
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(Number(number)));
setList(nextList);
setNumber('');
}, [number, list]); // number 혹은 list가 바뀌었을 때만 함수 생성
useCallback의 첫 번째 파라미터에는 함수를 넣고, 두 번째 파라미터에는 배열을 넣어줍니다.
해당 배열을 통해 어떤 값이 바뀌었을 때 함수를 새로 생성해줄 지 결정되고, 빈 배열을 넣는다면 처음 렌더링될 때만 함수를 생성해줍니다.
useRef
- 함수 컴포넌트에서 ref를 쉽게 사용할 수 있도록 해줌
Average 컴포넌트에서 '등록' 버튼 클릭 시, 포커스가 인풋으로 넘어가도록 해보겠습니다.
Average.js
import { useState, useMemo, useCallback, useRef } from "react";
const getAverage = numbers => {
console.log('평균값 계산 중...');
if(numbers.length === 0) return 0;
const sum = numbers.reduce((agg, num) => agg + num);
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(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;
등록 처리 후 포커스가 인풋 박스에 가있는 것을 확인할 수 있습니다.
이렇듯, useRef를 사용해 ref를 설정하면 useRef를 통해 만든 객체 안의 current 값이 실제 엘리먼트를 가리킵니다.
로컬 변수 사용하기
로컬변수란?
렌더링 상관없이 바뀔 수 있는 값을 의미합니다.
클래스 형태로 작성된 컴포넌트의 경우, 로컬 변수를 사용해야할 때 아래와 같이 작성합니다.
import { Component } from "react";
class MyComponent extends Component {
id = 1;
setId = (n) => {
this.id = n;
}
printId = () => {
console.log(this.id);
}
render() {
return (
<div>
MyComponent
</div>
)
}
}
export default MyComponent;
이를 함수 컴포넌트로 작성해보겠습니다.
import { useRef } from "react";
const RefSample = () => {
const id = useRef(1);
const setId = (n) => {
id.current = n;
}
const printId = () => {
console.log(id.current);
}
return (
<div>
refsample
</div>
)
}
export default RefSample;
ref 안의 값이 변경되어도 컴포넌트가 렌더링 되지 않으므로, 렌더링과 관련되지 않은 값을 관리할 때만 위 방법을 사용해줍니다.
커스텀 Hooks 만들기
여러 컴포넌트에서 비슷한 기능을 공유할 경우, 커스텀 Hook으로 작성하여 로직을 재사용할 수도 있습니다.
useInputs.js
import { useReducer } from "react";
function reducer(state, action) {
return {
...state,
[action.name]: [action.value]
}
}
export default function useInputs(initialForm) {
const [state, dispatch] = useReducer(reducer, initialForm);
const onChange = e => {
dispatch(e.target);
}
return [state, onChange];
}
Info.js
import useInputs from "./useInputs";
const Info = () => {
const [state, onChange] = useInputs({ name: '', nickname: '' });
const { name, nickname } = state;
return (
<div>
<div>
<input name="name" value={name} onChange={onChange} />
<input name="nickname" value={nickname} onChange={onChange} />
</div>
<div>
<div>
<b>이름 : </b> {name}
</div>
<div>
<b>닉네임 : </b> {nickname}
</div>
</div>
</div>
)
};
export default Info;
이처럼 커스텀 Hook을 이용하여 코드가 깔끔해진 것을 확인할 수 있습니다.
'JavaScript > React' 카테고리의 다른 글
React) 컴포넌트 스타일링② - Sass 사용하기 (0) | 2022.11.18 |
---|---|
React) 컴포넌트 스타일링① - 일반 CSS 방식 (0) | 2022.11.18 |
React) Hooks - useReducer, useMemo (0) | 2022.11.15 |
React) Hooks - useState, useEffect (0) | 2022.11.15 |
React) 컴포넌트의 라이프사이클 메소드 (0) | 2022.11.14 |