처음 배우는 리액트 네이티브 - 김범준 정리
컴포넌트 상태를 관리하기 위한 useState
const [count, setCount] = useState(0);
src/components/Counter.js
import React, { useState } from "react";
import styled from 'styled-components/native';
import Button from "./Button";
const StyledText = styled.Text`
font-size: 24px;
margin: 10px;
`;
const Counter = () => {
const [count, setCount] = useState(0);
return (
<>
<StyledText>count: {count}</StyledText>
<Button
title="+"
onPress={() => {
setCount(count +1);
}}
/>
<Button
title="-"
onPress={() => {
setCount(count -1);
}}
/>
</>
);
};
export default Counter;
숫자 상태를 나타내는 count를 useState로 생성, Button 컴포넌트를 이용하여 클릭마다 세터 함수를 이용해 상태 변경
setState(prevState => {});
전달된 함수에는 변경되기 전 상태값이 파라미터로 전달 됨
이 값을 어떻게 수정할 지 함수로 정의
세터 함수는 비동기로 동작하기 때문에 상태 변경이 여러번 일어날 경우 상태가 변경되기 전에 상태에 대한 업데이트가 일어나서 의도대로 동작하지 않을 수 있음
바로바로 변경 x
이를 해결하기 위해서 세터 함수에 함수를 인자로 전달하여 이전 상태값을 이용할 수 있음
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
이렇게 코드를 작성하면 두 번 호출했을 때 count가 의도대로 2번 증가함
컴포넌트가 렌더링될 때 마다 원하는 작업이 실행되도록 설정할 수 있는 기능
useEffect(() => {}, []);
첫 번째 파라미터로 전달된 함수는 조건을 만족할 때마다 호출
두 번째 파라미터로 전달되는 배열을 이용해 함수 호출 조건 설정
import React, { useState, useEffect } from 'react';
import styled from 'styled-components/native';
const StyledTextInput = styled.TextInput.attrs({
autoCapitalize: 'none',
autoCorrect: false,
})`
border: 1px solid #757575;
padding: 10px;
margin: 10px 0;
width: 200px;
font-size: 20px;
`;
const StyledText = styled.Text`
font-size: 24px;
margin: 10px;
`;
const Form = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
useEffect(() => {
console.log(`name: ${name}, email: ${email}\n`);
});
return (
<>
<StyledText>Name: {name}</StyledText>
<StyledText>Email: {email}</StyledText>
<StyledTextInput
value={name}
onChangeText={text => setName(text)}
placeholder="name"
/>
<StyledTextInput
value={email}
onChangeText={text => setEmail(text)}
placeholder="email"
/>
</>
);
};
export default Form;
useEffect(() => {
console.log(`name: ${name}, email: ${email}\n`);
}, [email]);
두 번째 파라미터를 email로 지정
email이 변할 때만 log 생김
useEffect(() => {
console.log(`\n===== Form Component Mount =====\n`);
}, []);
useEffect(() => {
console.log(`\n===== Form Component Mount =====\n`);
return () => console.log('\n===== Form Component Unmount =====\n');
}, []);
const App = () => {
const [isVisible, setIsVisible] = useState(true);
return (
<Container>
<Button
title={isVisible ? 'Hide' :'Show'}
onPress={() => setIsVisible(prev => !prev)}
/>
{isVisible && <Form />}
</Container>
);
};
특정 DOM을 선택해야할 때 사용하는 getElementById 처럼 특정 컴포넌트를 선택해야 할 때 사용
예를 들어 컴포넌트로 포커스를 설정하고 싶은 경우
const ref = useRef(initialValue);
주의할 점
const Form = () => {
...
const refName = useRef(null);
const refEmail = useRef(null);
...
useEffect(() => {
console.log(`\n===== Form Component Mount =====\n`);
refName.current.focus();
return () => console.log('\n===== Form Component Unmount =====\n');
}, []);
return (
<>
...
<StyledTextInput
...
ref={refName}
returnKeyType='next'
onSubmitEditing={() => refEmail.current.focus()}
/>
<StyledTextInput
...
ref={refEmail}
returnKeyType='done'
/>
</>
);
};
키보드의 완료 버튼을 각각 next와 done으로 변경하고 마운트 될 때 name 에 포커스가 있도록, next를 누르면 email로 포커스를 옮기도록 설정함
동일한 연산의 반복 수행을 제거하여 성능을 최적화 시킴
useMemo(() => {}, []);
첫 번째 파라미터에 함수 전달
두 번째 파라미터에 함수 실행 조건을 배열로 전달하면 지정된 값에 변화가 있는 경우에만 함수가 호출
useEffect 사용 전
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]);
};
return(
<>
<StyledText>Text: {text}</StyledText>
<StyledText>Length: {length}</StyledText>
<Button title="Get Length" onPress={_onPress} />
</>
);
}
export default Length;
useEffect 사용 후
...
const Length = () => {
const [text, setText] = useState(list[0]);
const _onPress = () => {
++idx;
if (idx < list.length) setText(list[idx]);
};
const length = useMemo(() => getLength(text), [text]);
...
}
export default Length;
useMemo를 사용해 text의 값이 변경되었을 때만 text의 길이를 구함
중복 연산을 방지함으로써 성능을 최적화
Fetch를 사용하여 useFetch라는 이름의 Hook 만들기
useFetch.js
import { useState, useEffect } from "react";
export const useFetch = url => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(url);
const result = await res.json();
if (res.ok) {
setData(result);
setError(null);
} else {
throw result;
}
} catch (error){
setError(error);
}
};
fetchData();
}, []);
return {data, error};
};
useEffect에 비동기함수를 첫 번째 파라미터로 전달할 경우 함수 내부에 비동기함수를 정의하고 사용하는 방법으로 경고를 방지할 수 있음
Dog.js
import React from 'react';
import styled from 'styled-components/native';
import { useFetch } from '../hooks/useFetch';
const StyledImage = styled.Image`
background-color: #7f8c8d;
width: 300px;
height: 300px;
`
const URL = 'https://dog.ceo/api/breeds/image/random';
const Dog = () => {
const {data,error} = useFetch(URL);
return(
<>
<StyledImage source={data?.message ? {uri: data.message} : null} />
</>
);
};
export default Dog;
이때 data?.message 는 옵셔널 체이닝 문법으로 data 속성이 있을 경우에만 접근하여 TypeError를 방지하며 안전하게 접근하는 문법임
강아지 api
Dogs API https://dog.ceo/api/breeds/image/random
useFetch.js
...
const [inProgress, setInProgress] = useState(false);
...
useEffect(() => {
const fetchData = async () => {
try {
setInProgress(true);
...
} catch (error){
setError(error);
} finally {
setInProgress(false);
}
};
fetchData();
}, []);
return {data, error, inProgress};
};
Dog.js
...
const Loading = styled.Text`
font-size: 18px;
color: #2ecc71;
`
const URL = 'https://dog.ceo/api/breeds/image/random';
const Dog = () => {
const {data,error, inProgress} = useFetch(URL);
return(
<>
{inProgress && (
<Loading>The API request is in progress</Loading>
)}
...
</>
);
};
export default Dog;
api 요청 중 로딩 메세지를 띄워주도록 코드 추가