** 본 정리는 실전 리액트 프로그래밍(저자 : 이재승) 으로 부터 작성하였습니다 **
리액트 훅은 기존에 리액트가 갖고 있던 여러 가지 문제를 해결해 준다.
함수형 컴포넌트는 리액트 팀에서도 적극적으로 기능 개발에 많은 시간을 투자하고 있기 때문에
당장 클래스 컴포넌트로 구현한 프로젝트가 아니라면 시작할 때 함수형 컴포넌트로 대체하는 것이 좋다.
리액트 훅?
훅이란 함수형 컴포넌트에서도 클래스형 컴포넌트의 기능을 사용할 수 있게 하는 기능이다.
훅을 통해서 함수형 컴포넌트에서도 컴포넌트의 상탯값을 관리할 수 있고, 생명주기 함수를 이용할 수 있다.
훅을 사용하면 재사용 가능한 로직을 쉽게 만들 수 있으며,
클래스형 컴포넌트와는 다르게 중복된 코드가 서로 다른 메서드에 들어가지 않는다.
useState 훅
useState 훅은 컴포넌트의 상탯값을 관리하는데 사용된다.
import React, { useState } from 'react';
function component({props}){
const [name, setName] = useState('jh0956');
return(
<div>
<div>{`name is ${name}`}</div>
<input type="text" value={name} onChange={e=>setName(e.target.value)} />
</div>
)
}
[name, setName] 으로 선언하는 방식은 1개의 상태값을 사용하는 단순 형태이다.
useState('jh0956') 은 name의 기본값을 'jh0956'으로 지정한다는 초기화이다.
여기서 name은 상탯값의 이름이 되고, setName은 name이라는 상탯값을 변경하기 위한 함수이다.
또한 위의 방법으로 여러개의 useState 훅을 사용할 수도 있다.
하지만 좀더 간단하게 useState 훅 1개로 여러개의 useState 훅을 사용할 수도 있다.
import React, { useState } from 'react';
function component({props}){
const [state, setState] = useState({
name : 'jh0965',
age : 25
});
return(
<div>
<div>{`name is ${state.name}}</div>
<div>{`age is ${state.age}}</div>
<input
type="text"
value={state.name}
onChange={e => setState({...state, name : e.target.value})} />
...
</div>
)
}
배열 비구조화 방식에서 state라는 변수는 '객체'가 되고 setState는 state 객체의 각 상태값의 변경을 위한 함수가 된다.
위 코드에서 onChange에서 setState를 호출하게 되는데
useState의 방식은 현재 받는 매개인자를 원래 상태값에 덮어버리는
즉 원래 상태값은 전부 지워버리고 받은 매개인자를 추가하는 방식이다.
그래서 전개연산을 통해 ...state의 기존 상태값을 주고, 그 중 name의 상태값만 변경하도록 코드해야한다.
생명주기 함수 useEffect
클래스형 컴포넌트의 생명주기 메서드는 서로 연관성이 없는 여러 기능이 하나의 생명 주기 메서드에 섞이는 등의
여러가지 문제점이 있다. useEffect를 사용하면 비슷한 기능을 한곳으로 모을수 있어서 가독성이 좋아진다.
useEffect 사용법
import React, { useState, useEffect } from 'react';
function component({props}){
const [count, setCnt] = useState(0);
useEffect( () => {
document.title = `업데이트 횟수: ${count}`;
});
return <button onClick = { () => setCnt(count+1) }>increase</button>;
}
** useEffect 훅에 입력된 함수는 렌더링 결과가 실제 돔에 반영된 후 호출된다 **
따라서 document.title 을 변경하는 코드를 클래스형 컴포넌트의 componentDidMount와 componentDidUpdate
양쪽 메서드에 추가하면 같은 기능을 하게 된다.
클래스형 컴포넌트에서는 서로 다른 로직이 하나의 생명주기 메서드에서 작성되었지만, useEffect 훅을 이용하면 하나의 로직을 위한 코드를 한 곳으로 모을 수 있다.
버튼을 클릭할 때마다 다시 렌더링되고, 렌더링이 끝나면 useEffect 훅에 입력된 함수가 호출된다.
API를 호출하는 기능: 함수형 컴포넌트로 작성하기
클래스형 컴포넌트의 생명 주기 메서드에는 API를 호출하는 코드가 자주 포함된다.
함수형 컴포넌트에서도 훅을 이용해서 다음과 같이 API를 호출할 수 있다.
import React, {useState,useEffect} from 'react';
function component({props}){
const [user, setUser] = useState('');
useEffect( () => {
getUserAPI(props).then(data => serUser(data));
}, [props] // here!
);
return(
<div>
{!user && <div> 사용자 정보를 가져오는 중... </div>}
{user && (
<div>
<!-- name과 age는 API 통신의 결과로 받아온 데이터 -->
<p>{`name is ${user.name}`}</p>
<p>{`age is ${user.age}`}</p>
</div>
</div>
)
}
useEffect 훅에서 API통신을 가정한 코드이며 useState로 받아온 데이터를 저장한다.
주석처리한 here 부분을 보자
useEffect 훅에 입력된 함수는 렌더링 될 때마다 호출되기 때문에 API 통신을 불필요하게 많이 하게 된다.
이를 방지하기 위해 useEffect 훅의 2번째 매개변수로 배열을 입력하면 배열의 값이 변경되는 경우에만 함수가 호출된다
그렇기 때문에 위의 코드는 배열 props가 변경되는 경우에만 API 통신을 하도록 설정한다.
만약 위 코드를 클래스형으로 작성했다면 componentDidMount와 componentDidUpdate 두 메서드에 모두 작성했을 것이다.
useEffect 이벤트 처리 함수를 등록하고 해제하는 기능
클래스형 컴포넌트에서는 componentDidMount 에서 구독을 하고 componentWillUnMount에서 해제하는 방식이 일반적이다. 서로 다른 로직이 하나의 생명 주기 메서드에 추가되면서 코드는 복잡해지고 실수가 발생하기 쉽다.
useEffect 훅을 이용하면 이러한 등록과 해제 절차를 한 곳에서 관리할 수 있다.
import React, { useState, useEffect } from 'react';
function component(){
const [width, setWidth] = useState(window.innerWidth);
useEffect( () => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', onResize);
return () => { window.removeEventListener('resize', onResize};
}, []); // here!!
return <div> {`width is ${width}`} </div>
}
위 코드는 창 크기가 변경될 때마다 onResize 함수가 호출된다.
useEffect 훅의 첫 번째 매개변수에 등록된 함수가 또 다른 함수를 반환할 수 있다.
반환된 함수는 컴포넌트가 unMount 되거나 첫 번째 매개변수로 입력된 함수가 호출되기 직전에 호출된다.
따라서 첫 번째 매개변수로 입력된 함수가 반환한 함수는 프로그램이 비정상적으로 종료되지 않는다면 반드시 호출될 것이 보장된다.
주석 here을 보자
useEffect 훅의 2번째 매개변수에 빈 배열을 넣으면 컴포넌트가 Mount 될 때만 첫 번째 매개변수로 입력된 함수가 호출되고, 컴포넌트가 unMount 될때만 반환된 함수가 호출된다.
=> 즉 이 코드는 componentDidMount와 componentWillUnMount 메서드에서만 실행되는 것과 같은 맥락이다.
위에서 API 호출, 이벤트 리스너를 예로 들어 설명하였다.
그리고 다음처럼 코드별로 분리해서 useEffect를 작성할 수도 있다.
import React, { useState, useEffect } from 'react';
function component({props}){
const [width, setWidth] = useState(window.innerWidth);
const [user, setUser] = useState('');
useEffect( () => {
getUserApi(props).then(data => setUSer(data));
}, [props]);
useEffect( () => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', onResize);
return () => { window.removeEventListener('resize', onResize};
}, []);
return ..
}
만약 클래스형 컴포넌트로 작성했다면, 서로 다른 여러 로직이 분리되어 있지 않아 가독성이 떨어짐을 알게 된다.
Custom Hook!
리액트가 제공하는 훅을 이용해서 custom 훅을 만들 수 있다.
그리고 커스텀 훅을 이용해서 또 다른 커스텀 훅을 만들 수도 있다.
훅을 직접 만들어서 사용하면 고차 컴포넌트와 렌더 속성값 패턴처럼 로직을 재사용할 수 있다.
* 커스텀 훅을 만들 때는 use로 시작하도록 변수명을 정하자.
위에서 창의 너비를 관리하는 이벤트 리스너 코드를 가지고 커스텀 훅을 만들어보자.
이름은 useWindowWidth
import React, { useState, useEffect } from 'react';
function useWindowWidth(){
const [width, setWidth ] = useState(window.innerWidth);
useEffect( () => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
},[]);
return width;
}
useWindowWidth 함수는 useState, useEffect로 만들어 졌으며
마지막에 width를 반환하는 커스텀 훅으로 코드되었다.
방금 만든 useWindowWidth 훅은 이렇게 사용할 수 있다.
import React, {useState} from 'react';
import { useWindowWidth } from './hooks';
function component(){
const width = useWindowWidth();
const [name, setName] = useState('');
return(
<div>
<p>{`name is ${name}`}</p>
<width < 600 && <br />}
<input type='text' value={name} onChange={e => setName(e.target.value)}/>
</div>
)
}
useWindowWidth를 사용하므로 창의 너비가 바뀔 때마다 새로운 창의 너비와 함께 재렌더링 된다.
'web > React' 카테고리의 다른 글
create-react-app 정리 (0) | 2020.12.30 |
---|---|
CSS 단위(responsive Units) (0) | 2020.09.16 |
fetch request 비동기통신 (0) | 2020.09.01 |
React NodeJs express 연동 (0) | 2020.09.01 |
바벨(babel) (0) | 2020.08.22 |