오늘은 <처음 배우는 리액트 네이티브>
6장 Hooks에 대해 알아보겠다 😁
Hooks
리액트 Hooks은 리액트 16.8, 리액트 네이티브 0.59 버전부터 사용할 수 있는 기능이다. Hooks를 이용해서 함수형 컴포넌트에서도 상태를 관리할 수 있게 되었다.
1. useState
주로 컴포넌트들의 상태를 관리하기 위해 useState
함수를 사용한다.
const [state, setState] = useState(initialState);
useState
함수를 호출하면 파라미터로 전달한 값을 초깃값으로 갖는 상태 변수(state)와 그 변수를 수정할 수 있는 세터 함수(setState)를 배열로 반환한다. useState
함수는 관리해야 하는 상태의 수만큼 여러 번 사용할 수 있다.
✅ 상태를 관리하는 변수는 반드시 세터 함수를 이용해 값을 변경해야 한다.
useState에서 반환된 세터 함수를 사용하는 방법
1️⃣ 하나는 세터 함수에 변경될 상태의 값을 인자로 전달하는 방법이다.
// Counter 컴포넌트
const [count, setCount] = useState(0);
return (
...
<Button
title="+"
onPress={() => {
setCount(count + 1);
setCount(count + 1);
...
}}/>
)
💥 하지만 여기서 문제가 발생한다. 세터 함수는 비동기로 동작하기 때문에 상태 변경이 여러 번 일어날 경우 상태가 변경되기 전에 또 다시 상태에 대한 업데이트가 실행되는 상황이 발생한다.
❗ 즉 세터 함수는 비동기로 동작해서 세터 함수를 호출해도 바로 상태가 변경되지 않는 문제가 발생하고, 이런 경우 다음 두 번째 방법을 사용해서 해결할 수 있다. 바로 세터 함수에 함수를 인자로 전달하여 이전 상태값을 이용하는 방법이다.
위 Counter
컴포넌트에서 + 버튼을 클릭했을 때 세터 함수를 두 번 호출해도 1만 증가된다. 원래 의도라면 2가 증가해야 한다.
2️⃣ 세터 함수를 이용하는 두 번째 방법은 바로 세터 함수에 함수를 인자로 전달하여 이전 상태값을 이용하는 방법이다.
// Counter 컴포넌트
const [count, setCount] = useState(0);
return (
...
<Button
title="+"
onPress={() => {
setCount(prevCnt => prevCnt + 1);
setCount(prevCnt => prevCnt + 1);
...
}}/>
)
이렇게 이전 상태의 값에 의존하여 상태를 변경할 경우, 세터 함수에 함수를 인자로 전달하여 이전 값을 이용하도록 작성해야 문제가 생기지 않는다.
2. useEffect
useEffect
는 컴포넌트가 렌더링될 때마다 원하는 작업이 실행되도록 설정할 수 있는 기능이다.
예시코드
useEffect(() => {}, []);
useEffect
의 첫 번째 파라미터로 전달된 함수는 조건을 만족할 때마다 호출되고, 두 번째 파라미터로 전달되는 배열을 이용해 함수가 호출되는 조건을 설정한다.
두 번째 파라미터의 배열이 어떤지에 따라서 경우를 다음과 같이 나눠서 살펴보겠다.
1️⃣ 두 번째 파라미터에 어떤 값도 전달하지 않는 경우
useEffect
의 첫 번째 인자로 전달된 함수는 컴포넌트가 렌더링될 때마다 호출된다.
2️⃣ 특정 상태가 변경될 때만 호출하고 싶은 경우
다음 코드를 보며 알아보겠다.
const Form = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
...
useEffect(() => {
console.log('name: ${name}, email: ${email}');
}, [email]);
...
};
...
위에서 useEffect
의 두 번째 파라미터로 email
을 지정해 email
의 상태가 변경되었을 때만 함수가 실행된다.
3️⃣ 마운트될 때 실행하기
useEffect
에 전달된 함수의 실행 조건이 컴포넌트가 마운트(mount)될 때로 설정하려면 두 번째 파라미터로 빈 배열 [ ]을 전달하면 된다.
✅ 잠깐! 여기서 컴포넌트가 마운트된다는 것은 컴포넌트가 처음 렌더링 되는 것을 의미한다. 즉 마운트될 때만 실행시킨다는 것은 최초로 렌더링 될 때 딱 한 번만 함수를 호출하는 것을 의미한다.
4️⃣ 언마운트될 때 실행하기
useEffect
에서 전달하는 함수에서 반환하는 함수, 즉 첫 번째 인자에서 return이 호출하는 함수를 정리(cleanup) 함수라고 한다. useEffect
의 두 번째 인자가 빈 배열 [ ]일 경우 컴포넌트가 언마운트 될 때 정리 함수를 실행시킨다.
3. useRef
리액트 네이티브에서 특정 컴포넌트를 선택해야 하는 경우에 사용한다. 예를 들어, 컴포넌트로 포커스를 설정하고 싶을 때 useRef
를 사용해서 해당 컴포넌트를 선택할 수 있다.
예시코드
const ref = useRef(initialValue);
useRef
를 사용할 때 주의할 점은 다음 두 가지이다.
- 컴포넌트의
ref
로 지정하면 생성된 변수에 값이 저장되는 것이 아니라 변수의.current property
에 해당 값을 담는다. useState
를 이용하여 생성된 상태와 달리useRef
의 내용이 변경돼도 컴포넌트는 다시 렌더링 되지 않는다.
import React, {useState, useEffect, useRef} from 'react';
const Form = () => {
...
const refName = useRef(null);
const refEmail = useRef(null);
useEffect(() => {
console.log('\n===== Form Component Monut =====\n');
refName.current.focus();
return () => console.log('\n===== Form Component Unmount =====\n');
}, []);
...
return (
<>
...
<StyledTextInput
...
ref = {refName}
returnKeyType = "next"
onSubmitEditing = {() => refEmail.current.focus()}
/>
<StyledTextInput
...
ref = {refEamil}
returnKeyType = "done"
/>
);
};
...
위 코드에서 useRef
함수를 사용해서 refName
과 refEmail
을 생성해 각각 이름과 이메일을 입력받는 TextInput
컴포넌트의 ref
로 설정했고, 키보드의 완료 버튼을 각각 next
와 done
으로 변경했다.
이름을 입력받는 컴포넌트의 확인(next) 버튼을 클릭하면 이메일을 입력받는 컴포넌트로 포커스가 이동하고, Form
컴포넌트가 마운트될 때 포커스가 이름을 입력받는 컴포넌트에 있도록 했다.
4. useMemo
useMemo
는 동일한 연산의 반복 수행을 제거해서 성능을 최적화하는 데 사용된다.
예시코드
useMemo(() => {}, []);
첫 번째 파라미터에는 함수를 전달하고, 두 번째 파라미터에는 함수 실행 조건을 배열로 전달한다. 이때 지정된 값에 변화가 있는 경우에만 함수가 호출된다.
다음 코드에서 문자열의 길이를 계산하는 Length
컴포넌트를 만들어보겠다.
const StyledText = styled.Text`
font-size: 24px;
`;
const getLength = text => {
console.log('Target Text: ${text}');
return text.length;
};
const list = ['Javascript', 'Expo', 'Expo', 'React Native'];
let idx = 0;
const Length = () => {
const [text, setText] = useState(list[0]);
const [length, setLength] = useState('');
const _onPress = () => {
setLength(getLength(text));
++idx;
if(idx < list.length) setText(list[idx]);
};
const length = useMemo(() => getLength(text), [text]);
return (
<>
<StyledText>Text: {text}</StyledText>
<StyledText>Length: {length}</StyledText>
<Button title="Get Length" onPress={_onPress} />
</>
);
};
export default Length;
위에서 Javascript, Expo, Expo, React Native로 이루어진 배열을 생성하고, 버튼을 클릭할 때마다 배열을 순환하며 문자열의 길이를 구하는 컴포넌트를 작성했다. useMemo
함수를 사용해서 text
의 값이 변할 때만 getLength
함수를 호출하도록 했다.
'Develop > React Native' 카테고리의 다른 글
[React Native] 리액트 네비게이션 라이브러리 사용하기 (stack, tab) (2) | 2021.11.21 |
---|---|
[React Native] 리액트 네이티브에서 전역 상태 관리하는 방법 - Context API, useContext (0) | 2021.11.14 |
[React Native] 리액트 네이티브로 ✅TODO List 만들기 (0) | 2021.11.14 |
Unable to resolve module @react-native-async-storage/async-storage 에러 해결 (0) | 2021.11.07 |
[React Native] 윈도우에서 React Native 개발환경 세팅하기, expo 프로젝트 생성하기 (Expo CLI) (0) | 2021.09.08 |