리액트를 다루는 기술 + typescript 8장 3
useCallback
- Typescript Version
-
주로 렌더링 성능을 최적화해야 될 때 사용합니다
-
useCallback
을 사용하면 함수를 재사용 할 수 있습니다. -
작성했던
Average
컴포넌트에는onChange
,onInsert
함수가 있는데,
컴포넌트가 리렌더링 될 때 마다 두 함수가 새로 만들어 집니다. -
컴포넌트의 렌더링이 매우 많을 경우, 이는 최적화가 필요하게 됩니다.
Average.tsx
import React, {useState, useMemo, useCallback} from 'react';
const getAverage = (numbers : number[]) : number => {
console.log('getAverage 실행');
if(numbers.length === 0) return 0;
const sum = number.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () : JSX.Element => {
const [list, setList] = useState<number[]>([]);
const [number, setNumber] = useState('');
const onChange = useCallback((e : React.ChangeEvent<HTMLInputElement>) => {
setNumber(e.target.value);
}, []); // 컴포넌트가 처음 렌더링 될때만 함수를 생성!
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
}, [number, list]);
// number or list가 바뀔 때만 함수 생성
// 함수 내부에 state 변수가 있으면 무조건 넣어주세요!
const avg = useMemo(() => getAverage(list), [list]);
return (
(...)
)
}
useCallback
에 대해 설명해 보겠습니다 - 책을 참고
Example => useCallback(callback, [])
-
1번째 파라미터 : 생성할 함수를 넣습니다.
-
2번째 파라미터 : 배열에 들어간 변수가 변할 때에 함수를 재생성
- 함수 내부에
state
값이 있다면 99% 배열 안에 넣어줍니다. - 배열이 비었다면, 처음 렌더링에만 생성하여 재사용합니다.
- 함수 내부에
useRef
- Typescript Version
-
ref
속성은HTML DOM
속성을 직접 건드리기 위해 필요한 속성입니다. -
useRef
속성은 함수 컴포넌트에서ref
를 쉽게 사용하게 만들어 줍니다.
Average.tsx
import React, {useState, useMemo, useCallback, useRef} from 'react';
const getAverage = (numbers : number[]) : number => {
console.log('getAverage execute');
if(numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () : JSX.Element => {
const [list, setList] = useState<number[]>([]);
const [number, setNumber] = useState('');
// 아직 'ref'가 지정되지 않았으므로, 초기화를 위해 null을 넣어줍니다.
// null을 넣지 않으면 JSX 문법에서 오류가 납니다.
const inputEl = useRef<HTMLInputElement>(null);
const onChange = useCallback((e : React.ChangeEvent<HTMLInputElement>) => {
setNumber(e.target.value);
}, []); // 첫 렌더링 시에만 생성한다는 의미
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber('');
// 1. 에서 설명
if(inputEl.current){
inputEl.current.focus();
}
}, [numbers, list]);
// numbers 그리고 list가 변경 될 때에만 재생성
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange} ref={inputEl}>
(...)
</div>
)
}
-
-
if(inputEl.current)
는 왜 선언되었을까요? useRef()
로 선언할 때,useRef(null)
하였기에,null
값도 동시에 가지고 있습니다.- 근데,
null
값은current
값을 가질 수 없습니다. javascript
는 자동으로 타입을 추정하여 오류를 일으키지 않지만,
typescript
는 사용하려는 객체가null
이 아니라는 것을 명시해야 합니다.
-
커스텀 Hooks
만들기
-
책에 쓰여져 있는 내용을 바탕으로
Typescript
를 적용하고 있습니다. -
책에 쓰여진 커스텀 훅은 추후에 배울 리덕스의 알고리즘과 어느정도 비슷합니다!
-
굳이 작성할 필요가 있을까 고민했지만 필요할 분이 있을 거라 생각해서
작성하겠습니다. -
밑의 예제는 여러 개의
<input>
을 관리하는 커스텀Hook
입니다.
useInput.ts
import React, {useReducer} from 'react';
interface State{
name : string;
nickname : string;
}
interface Action{
name : string;
value : string
}
function reducer(state : State, action : Action) {
return {
...state,
[action.name] : action.value
}
}
// custom hook Export!
export default function useInputs(initialForm : State) {
const [state, dispatch] = useDispatch(reducer, initialForm);
const onChange = (e : React.ChangeEvent<HTMLInputElement>) => {
dispatch(e.target);
}
return [state, onChange];
}
-
이 파일의 확장자명은
.ts
입니다! -
JSX
문법으로 만들어진 컴포넌트를 반환하는 것이 아니기 때문입니다. -
State
,Action
인터페이스를 따로 빼둔 모습입니다. -
export default
설정 되 있으므로, 이 파일을import
하면
기본으로useInputs
를 바로 사용할 수 있습니다.
Info.tsx
import React from 'react';
import useInputs from './useInputs';
const Info = () : JSX.Element => {
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>
)
}
-
컴포넌트 코드 내부에서
useReducer
를 사용하지 않고,
외부 파일에 맡긴 모습을 보여줍니다. -
기존의 사용 방식과 동일하게 선언하면 됩니다.