IT_World
[React] 함수형 컴포넌트 본문
함수형 컴포넌트란 함수를 기반으로 작성하는 컴포넌트를 말한다.기존에 우리가 사용했던 클래스형 컴포넌트에 비해 훨씬 짧고 직관적인 코드를 짤 수 있다.아래에서 나오는 Hooks가 도입되면서 함수형 컴포넌트에서도 클래스형 컴포넌트의 라이프 사이클 메서드와 같은 기능을 사용할 수 있게 되었다.
Hooks 란?
리액트 v16.8 로 업데이트되면서 추가된 기능으로서,함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 렌더링 직후 작업을 설정하는 useEffect 등의 기능 등을 제공한다.
useState
함수형 컴포넌트에서도 가변적인 상태를 지니고 있을 수 있게 해준다.함수형 컴포넌트 안에서 상태를 관리해야 하는 일이 생기면 이 Hooks를 이용하면 된다.
js 파일 내에서
import React, { useState } from 'react'; // import 로 useState 를 불러옴
const SayLove = () => { // 함수형 컴포넌트 start
const [value, setValue] = useState(0);
const [isModalActive, setIsModalActive] = useState(false);
return (
<div>
<p>
<b>{value}</b> 만큼 사랑합니다...
</p>
<button onClick={() => setValue(value + 1)}>+1</button>
<button onClick={() => setValue(value - 1)}>-1</button>
<button onClick={() => setIsModalActive(!isModalActive)}>
modal btn
</button>
</div>
);
};
export default SayLove;
:: 배열 비구조화 할당
배열 비구조화 할당의 쉬운 예시
const array = [1, 2];
const [one, two] = array;
console.log(one, two); // 1, 2
const [value, setValue] = useState(0);
예시에서 앞의 이 코드는 배열 비구조화 할당(객체 비구조화 할당이랑 비슷)
useState(0) 도 결국은 함수 뒤에 (0) 은 파라미터인데, 상태의 기본값위의 예시에서는 SayLove 컴포넌트의 기본 상태값을 0으로 설정해준것
이 useState(0) 함수는 실행후에 배열을 반환하는데, 이 배열의 첫번째값 value 와, 두번째값 setValue 을 배열 비구조화 할당을 이용해서 따로 선언해준다.
이때 [value, setValue] 는
- value : 원소의 현재 상태 값
- setValue : 상태를 설정하는 Setter 함수 이다.
const [value, setValue] = useState(0); // 는
const numberState = useState(0);
const number = numberState[0];
const setNumber = numberState[1]; // 이 3줄과 같음
즉, useState(0) 함수에 파라미터를 넣어서 호출하면 전달받은 파라미터로 값이 바뀌게 되고, 컴포넌트는 정상적으로 리렌더링 된다.
useState 여러번 사용하기
만약 컴포넌트에서 관리해야 할 상태가 여러 개라면 어떻게 해야 할까?
그냥 useState를 여러번 쓰는 방법이 있고, 하나의 useState 를 설정한 뒤, 여러개의 상태값을 관리하는 방법이 있다.
useState 를 여러번 쓰는 방법은, js 파일 내에서
const Info = () => {
const [name, setName] = useState('');
const [nickname, setNickname] = useState('');
const onChangeName = e => {
setName(e.target.value);
};
const onChangeNickname = e => {
setNickname(e.target.value);
};
return (
<div>
<div>
<input value={name} onChange={onChangeName} />
<input value={nickname} onChange={onChangeNickname} />
</div>
<div>
<div>
<b>이름:</b> {name}
</div>
<div>
<b>닉네임: </b>
{nickname}
</div>
</div>
</div>
);
};
이렇게 정말 여러개 써주는거고
useState 하나에 여러 상태를 관리하는 방법은, js 파일 내에서
import React, { useState } from 'react';
const Info = () => {
const [inputs, setInputs] = useState({
name: '',
nickname: ''
});
const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출
const onChange = (e) => {
const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
setInputs({
...inputs, // 기존의 input 객체를 전개 구문으로 펼쳐서 /복사한 뒤
[name]: value // name 키를 가진 값을 value 로 설정 (이때 [name]은 계산된 속성명 구문 사용)
});
};
const onReset = () => {
setInputs({
name: '',
nickname: '',
})
};
return (
<div>
<input name="name" placeholder="이름" onChange={onChange} value={name} />
<input name="nickname" placeholder="닉네임" onChange={onChange} value={nickname}/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
}
이렇게 useState 의 파라미터로 객체를 전달한다.
이때 주의할 점은, 리액트의 state에서 객체를 수정해야 할 때에는
inputs[name] = value;
이렇게 직접 수정하면 안되고,
setInputs({
...inputs,
[name]: value
});
이렇게 새로운 객체를 만들어서 새로운 객체에 변화를 주는 식으로 사용해야 한다.
아직은 어려운 개념이긴 한데.... react 의 불변성을 지키기 위해서 라고 한다.불변성을 지켜야지만, 리액트 컴포넌트에서 상태가 업데이트가 됐음을 감지 할 수 있고 이에 따라 필요한 리렌더링이 진행된다.inputs[name] = value 이렇게 기존 상태에 직접 접근해서 수정하면, 값이 바뀌어도 리렌더링 되지 않는다.또한, 불변성을 지켜야지만 컴포넌트의 업데이트 성능 최적화를 제대로 할 수 있다.
useEffect
useEffect 는 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정할 수 있는 기능이다.
쉽게 말하면, 클래스형 컴포넌트의 componentDidMount + componentDidUpdate 를 합친 형태라고 이해하면 된다.
import React, { useState, useEffect } from 'react';
const Info = () => {
const [inputs, setInputs] = useState({
name: '',
nickname: ''
});
useEffect(() => { // useEffect 적용
console.log('렌더링이 완료되었습니다');
console.log({
name,
nickname
});
});
const { name, nickname } = inputs;
const onChange = (e) => {
const { value, name } = e.target;
setInputs({
...inputs,
[name]: value
});
};
const onReset = () => {
setInputs({
name: '',
nickname: '',
})
};
return (
<div>
<input name="name" placeholder="이름" onChange={onChange} value={name} />
<input name="nickname" placeholder="닉네임" onChange={onChange} value={nickname}/>
<button onClick={onReset}>초기화</button>
<div>
<b>값: </b>
{name} ({nickname})
</div>
</div>
);
}
export default Info;
useEffect 컴디마처럼 사용하기
useEffect 에서 설정한 함수를 componentDidMount 처럼 사용하고 싶을때
즉, 컴포넌트가 화면에 가장 처음 렌더링 될 때만 실행되야 할 경우, (업데이트 때는 실행 할 필요가 없는 경우) 에는 함수의 두번째 파라미터로 비어있는 배열을 넣으면 된다.
위의 예시에서,
useEffect(() => {
console.log('렌더링이 완료');
console.log({
name,
nickname
});
});
를
// useEffect 안에 콜백 함수를 넣어준다.
useEffect(() => {
console.log('마운트 될 때만 실행됩니다.');
}, []); // [] 의 값이 바뀔때마다 계속 실행, 근데 빈배열은 즉 처음 한번만 실행된다.
이렇게 바꿔주면 끝
꼭 [] 을 넣어야만 하는건 아니다.
두번째 파라미터의 값이 변경될 때 첫번째 파라미터가 실행되는것이므로,
두번째 파라미터에 불변값을 넣어주면 되는데, 실제로 위 코드를useEffect(() => { console.log('마운트 될 때만 실행됩니다.'); }, "hello");
이렇게 작성하거나, "hello" 자리에 숫자등의 기본형 데이터 타입을 넣어도 잘 동작한다.
하지만, [] 빈 배열을 작성하는것이 컨벤션이므로 왠만하면 뻘짓하지 말고 [] 쓰기
fetching data
useEffect를 이용해서 data를 받아오기
useEffect( () => {
fetchInitialData(); // useEffect 안에서 바로 fetch를 사용하지 말고, fetch 역할의 함수를 실행할것
} )
const fetchInitialData = async () => {
const res = fetch('URL주소');
const initialData = await res.json();
setDatas(initialData);
}
setDatas(initialData) 를 통해 datas 상태에 초기값으로, fetch를 통해 받아온 데이터를 설정한다.
근데 이렇게만 하면, 렌더링이 계속계속계속 무한으로 반복된다.
왜? useEffect는 렌더링 된 직후에 실행되는데, 이 때 data를 fetch로 받아와서 초기 state를 setDatas 해주었고, setState가 일어나면 다시 렌더링 되기 때문에, 렌더링 직후에 실행되는 useEffect 가 또 실행되는것
그러니까 위의 예시처럼, 두번째 인자에 빈배열을 추가해준다.
useEffect( () => {
fetchInitialData();
}, [] )
loading data
실제 데이터가 받아와지는 동안 딜레이가 생기는 경우가 많다.
이때, loading 값을 관리하여 예쁜 로딩바나 로딩 애니메이션으로 사용자 경험을 높일 수 있다.
const [loading, setLoading] = useState(false);
loading state를 추가해주고,
const fetchInitialData = async () => {
setLoading(true); // data를 받아오기 시작할 때 loading 값을 true로
const res = fetch('URL주소');
const initialData = await res.json();
setDatas(initialData);
setLoading(false); // data를 받아오는게 끝날 때 loading 값을 false로
}
data를 받아오는 fetchInitialData 메서드에 다음과 같이 setLoading을 실행한다.
그리고 로딩창을 보여줄 component의 return 위에
let toDoList = 로딩 화면
if (!loading) toDoList = 로딩이 끝나면 보여줄 내용
이라고 작성해준다.
useEffect 컴디업처럼 사용하기
useEffect 에서 설정한 함수를 componentDidUpdate 처럼 사용하고 싶을때
즉, 컴포넌트의 특정 값이 변경될 때만 useEffect 를 호출하고 싶을 때에는 함수의 두번째 파라미터로 전달되는 배열 안에 검사하고 싶은 값을 넣으면 된다.
위의 예시에서,
useEffect(() => {
console.log('렌더링이 완료되었습니다.');
console.log({
name,
nickname
});
});
를
useEffect(() => {
console.log(name);
}, [name]); // [name] : [name] 값이 바뀔 때만 실행, 즉 처음 한번만 실행된다.
이렇게 바꿔주면 끝
참고로, [name] 이라는 배열 안에는 useState 를 통해 관리하고 있는 상태를 넣어줘도 되고, props 로 전달받은 값을 넣어줘도 된다.
참고로 위의 코드를 클래스형 컴포넌트에서 쓴다면 이렇게 쓸 수 있다.
componentDidUpdate(prevProps, prevState) {
if (prevProps.value !== this.props.value) {
doSomething();
}
}
:: useEffect 로 비동기 처리 하기
handleBtnColor = () => {
this.setState({
color: "red"
}, () => console.log(this.state.color))
}
class형 component에서는 이렇게 작성했다.
( state의 color 값이 바뀔때마다 console.log 함수를 실행)
const [color, setColor] = useState("blue")
const handleBtnColor = () => {
setColor("red")
}
useEffect(() => {
console.log(color)
}, [color])
function형 component에서는 이렇게 작성할 수 있다.
'Front-End > React' 카테고리의 다른 글
[React] React에 대해 정리2 (0) | 2022.05.19 |
---|---|
[React] React에 대해 정리 (0) | 2022.05.19 |
[React] Signin form validation check 적용 (0) | 2022.04.14 |
[React] Login / Password (0) | 2022.04.14 |
[React] Webhook (0) | 2022.04.12 |