아래 코드의 경우 2씩 올라가야 하는데 각각의 setter함수가 비동기로 동작하여 1씩만 증가한다.
const [count,setCount] = useState(0)
(...)
onPress={()=>{
setCount(count+1);
setCount(count+1)
}}
setter 함수에 함수를 전달해서 상태 변수의 현재 값을 바탕으로 변화를 주어야 한다. setCount 함수에 설정하는 값을 값이 아닌
함수 형태로 전달해서 상태변수의 현재 값을 바탕으로 변화를 주어야 한다.
const [count,setCount] = useState(0)
(...)
onPress={()=>{
setCount(count => {return count+1});
setCount(count => {return count+1})
}}
위의 코드처럼 진행하게되면 동기적으로 진행하여 2씩 증가하게 된다.
useRef hook을 사용하면 특정 컴포넌트를 선택할 수 있다.
- 컴포넌트의 ref에 지정을 하고 사용해야 한다. ex.
const refInput =useRef(null)- 변수를 바로 사용하는 것이 아니라 current property에 지정이 되기 때문에 반드시 current property를 이용해야 한다. ex.
<Input ref={refInput} />- useState와는 다르게 변경된다 하더라도 컴포넌트가 리렌더링 되지 않는다. ex.
refInput.current.focus()
import React, { useState, useEffect, useRef } from 'react';
const Form = () => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
//useRef를 이용해서 refName과 refEmail을 만들어줌
const refName = useRef(null);
const refEmail = useRef(null);
useEffect(() => {
//처음 렌더링 되었을때 refName컴포넌트에 포커스가 가도록 설정
refName.current.focus();
}, []);
return (
<>
<StyledText>Name : {name}</StyledText>
<StyledText>Email : {email}</StyledText>
<StyledInput
value={name}
onChangeText={setName}
placeholder='name'
//해당 컴포넌트를 refName으로 설정
ref={refName}
returnKeyType='next'
//next 버튼을 누르게 되면 reEmail로 설정된 컴포넌트로 포커스를 이동
onSubmitEditing={() => refEmail.current.focus()}
/>
<StyledInput
value={email}
onChangeText={setEmail}
placeholder='email'
//해당 컴포넌트를 refEmail로 설정
ref={refEmail}
returnKeyType='done'
onSubmitEditing={제출함수}
/>
</>
);
};
export default Form;
//hooks/useFetch.js
import { useState, useEffect } from 'react';
// Fetch를 활용한 API 요청을 보내는 간단한 Hook
export const useFetch = (url) => {
//파라미터로 url을 전달받음
const [data, setData] = useState(null);
const [error, setError] = useState(null);
// uesFetch에서 url을 전달받아 api 요청을 보내고 성공 여부에 따라 데이터 혹은 에러를 반환해야 한다.
// useEffect를 이용해서 api 호출을 할건데 비동기 통신이기에 async,await를 이용
useEffect(async () => {
try {
//fetch를 이용해서 response를 받음
//setData를 이용해서 데이터를 설정
const res = await fetch(url);
const result = await res.json();
setData(result);
} catch (e) {
// try, catch문을 이용해 에러 발생시 catch로 들오면 setError를 이용해 에러의 데이터를 설정
setError(e);
}
}, []); // 마운투 되었을때만 실행
return { data, error };
// 마지막으로 데이터와 에러를 반환해서 useFetch를 사용했을때 데이터와 에러를 반환
};
위의 코드로 진행시 아래와 같은 오류 코드를 볼 수 있는데 이 경고 메세지는 useEffect에 비동기 함수를 전달했을 때 나타나는 경고메세지입니다.
useEffect must not return anything besides a function, which is used for clean-up. It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, write the async function inside your effect and call it immediately:
useEffect에 전달되는 함수는 비동기 함수를 이용할 수 없기 때문에 비동기 함수를 사용해야 할 떄는 useEffect 내부에서 비동기 함수를 정의하고 호출하는 방법으로 해결할 수 있다.
위의 코드에서 useEffect에 전달되는 함수에서 async를 삭제하고 getData라는 함수를 새로만들어 줘서 async await를 이용해주었다.
import { useState, useEffect } from 'react';
export const useFetch = (url) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const getData = async () => {
try {
const res = await fetch(url);
const result = await res.json();
setData(result);
} catch (e) {
setError(e);
}
};
getData();
}, []);
return { data, error };
};
import { useState, useEffect } from 'react';
export const useFetch = (url) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
//api의 진행 여부를 확인하는 상태변수. 호출 시작 전이니 false로 설정
const [inProgress, setInProgress] = useState(false);
useEffect(() => {
const getData = async () => {
try {
setInProgress(true); // api호출이 시작되었으니 true로 설정
const res = await fetch(url);
const result = await res.json();
setData(result);
} catch (e) {
setError(e);
} finally {
//정상적으로 종료가 되든 에러가 생기든 api가 끝났으니 false 설정
setInProgress(false);
}
};
getData();
}, []);
return { data, error, inProgress };
};
import React, { useState } from 'react';
import CoinInfo from './components/CoinInfo';
import styled from 'styled-components/native';
import { useFetch } from './hooks/useFetch';
const Container = styled.View`
flex: 1;
background-color: #fff;
align-items: center;
justify-content: center;
`;
const LoadingText = styled.Text`
font-size: 30px;
color: #ff6600;
`;
const App2 = () => {
const URL = `https://api.coinlore.net/api/tickers/?limit=3`;
const { data, error, inProgress } = useFetch(URL);
return (
<Container>
// api가 호출되는 동안 Loading 텍스트 브라우저상에 보여줌
{inProgress && <LoadingText>Loading...</LoadingText>}
{data?.data.map(({ symbol, name, price_usd }) => (
<CoinInfo key={symbol} symbol={symbol} name={name} price={price_usd} />
))}
</Container>
);
};
export default App2;
