[React] 기본 Hook (useState, useEffect, useContext)
1. Hook 개요
2. State Hook 사용하기
3. Effect Hook 사용하기
4. Context Hook 사용하기
1. Hook 개요
Hook은 함수 컴포넌트에서 React state와 생명주기 기능(lifecycle)을 "연동(hook into)"할 수 있게 해주는 함수이다.
Hook은 class 안에서는 동작하지 않는다. Hook은 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있게 해준다.
(Hook은 React 16.8에 새로 추가된 기능이다.)
Hook은 일반적인 JavaScript 함수이지만, 두 가지 규칙을 준수해야 한다.
- 최상위(at the top level)에서만 Hook을 호출해야 한다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행할 수 없다.
- React 함수 컴포넌트 내에서만 Hook을 호출해야 한다. 일반 JavaScript 함수에서는 Hook을 호출하면 안된다.
2. State Hook 사용하기
import React, {useState} from 'react';
const State = () => {
const [value, setValue] = useState('hello');
const [count, setCount] = useState(0);
const handleClick = () => {
setValue('jenny');
};
return(
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<button onClick={handleClick}>버튼</button>
{value}
</div>
)
};
export default State;
여기서 useState()가 바로 Hook이다. Hook을 호출해 함수 컴포넌트 안에 state를 추가했다. 이 state는 컴포넌트가 다시 렌더링 되어도 그대로 유지된다.
useState는 현재의 state 값과 이 값을 업데이트하는 함수를 쌍으로 제공한다. 이 함수는 이벤트 핸들러나 다른 곳에서 호출할 수 있다. 이는 class의 this.setState와 거의 유사하지만, 이전 state와 새로운 state를 합치지 않는다는 차이점이 있다.
useState는 인자로 초기 state 값을 하나 받는다. useState('hello') 가 쓰인 곳에서는 state의 value 값을 hello로 초기 지정하였다. useState(0)이 쓰인 부분에서는 state의 count 초기값을 0으로 하겠다는 것이다.
'Click me' 버튼을 클릭할 때 마다 count 값이 1씩 증가하게 되어 그때의 count 값이 You clicked n times의 n으로 나타난다.
'버튼' 을 클릭하면 value가 'hello'에서 'jenny'로 바뀌면서 화면에도 'jenny' 글자가 나타난다.
3. Effect Hook 사용하기
import React, {useState, useEffect} from 'react';
const Effect = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = 'jenny';
console.log('hello world');
// return () => {
// console.log('bye');
// }
}, []);
return(
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount((prev) => prev + 1)}>
Click me
</button>
</div>
)
};
export default Effect;
React 컴포넌트 안에서 데이터를 가져오거나 DOM을 직접 조작하는 작업을 할 수 있다. 이런 모든 동작을 "side effects(또는 effects)"라고 한다. 왜냐하면 이러한 동작들이 컴포넌트에 영향을 줄 수도 있고, 렌더링 과정에서는 구현할 수 없는 작업이기 때문이다.
Effect Hook, 즉 useEffect는 함수 컴포넌트 내에서 이런 side effects를 수행할 수 있게 해준다. React class의 componentDidMount나 componentDidUpdate, componentWillUnmount와 같은 목적으로 제공되지만, 하나의 API로 통합된 것이다.
위의 코드를 살펴보면, useEffect는 Effect라는 함수 컴포넌트 안에 선언되어 있기 때문에 props와 state에 접근할 수 있다. 기본적으로 React는 매 렌더링 이후에 effects를 실행한다.
위 코드의 경우는 componentDidMount와 같다. 즉, 최초에 컴포넌트가 마운트 될 때 1번만 실행된다.
useEffect(() => {}, [count])와 같이 [] 안에 state 값을 넣으면 (최초 렌더링 했을 때를 포함) 해당 state 값이 바뀔 때 마다 useEffect가 실행된다. 이 경우는 componentDidUpdate와 같다.
위 코드의 주석을 해제하면 componentWillUnmount와 같아진다. 즉, 컴포넌트가 소멸된 시점에 (DOM에서 삭제된 후) 실행된다. 컴포넌트 내부에서 타이머나 비동기 API를 사용하고 있을 때, 이를 제거하기에 유용하다.
4. Context Hook 사용하기
import React, { useState, createContext, useContext } from "react";
export const Global = createContext();
const D = () => {
const {name, setName} = useContext(Global);
return(
<>
hello world {name}
<button onClick={() => setName('jiyoung')}>ChangeName</button>
</>
)
};
const C = ({name}) => {
return(
<D name={name}/>
)
};
const B = ({name}) => {
return(
<C name={name}/>
)
};
const A = ({name}) => {
return(
<B name={name}/>
)
};
const Context = () => {
const [name, setName] = useState('jenny');
const obj = {
name,
setName
};
return(
<>
<Global.Provider value={obj}>
<A name={name}/>
</Global.Provider>
</>
)
};
export default Context;
useContext는 context 객체(React.createContext에서 반환된 값)을 받아 그 context의 현재 값을 반환한다. context의 현재 값은 트리 안에서 이 Hook을 호출하는 컴포넌트에 가장 가까이에 있는 <Global.Provider>의 value prop에 의해 결정된다.
컴포넌트에서 가장 가까운 <Global.Provider>가 갱신되면 이 Hook은 그 Global provider에게 전달된 가장 최신의 context value를 사용하여 다시 렌더링 되게 한다. 상위 컴포넌트에서 React.memo 또는 shouldComponentUpdate를 사용하더라도 useContext를 사용하고 있는 컴포넌트 자체에서부터 다시 렌더링된다.
useContext로 전달한 인자는 context 객체 그 자체여야 한다. useContext(Global)과 같은 형태로 사용해야 하고, useContext(Global.Provider)나 useContext(Global.Consumer)처럼 사용할 수 없다.
useContext를 호출한 컴포넌트는 context 값이 변경되면 항상 리렌더링 된다.