React Hook이 나오기 전에는 state와 Life Cycle을 사용하기 위해서 컴포넌트를 Class로 만들어야 했습니다. 하지만, Class로 컴포넌트를 만들다 보니 로직이 복잡해지고, 가독성이 떨어져서 컴포넌트를 재사용하기가 어려웠습니다.
추후에 함수 컴포넌트에서도 state와 Life Cycle을 사용할 수 있는 Hook이 출시됐고, 컴포넌트의 로직과 가독성이 향상되어 재사용하기가 수월해졌습니다.
----
1. Hooks
- React 16.8 버전에 새로 추가된 기능이며, 클래스 컴포넌트에서만 state와 Life Cycle을 사용할 수 있던 걸, 함수 컴포넌트에서도 사용할 수 있게 해줍니다.
- 단순히 state와 Life Cycle을 사용할 수 있다는 점을 넘어서, 컴포넌트의 state와 관련된 로직을 재사용할 수 있다는 점이 강점입니다.
- https://reactjs.org/docs/hooks-intro.html
프로젝트 생성
npx create-react-app hooks-test
cd hooks-test
버튼을 클릭하면, 화면에 보이는 숫자가 1씩 증가하는 화면을 예시로 둘을 비교해보겠습니다.
기존의 클래스 컴포넌트
import React from 'react';
class ClassComponent extends React.Component {
state = { count: 0 };
click = () => {
this.setState({ count: ++this.state.count });
}
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
render() {
const { count } = this.state;
return (
<div>
<p>You Clicked {count} times</p>
<button onClick={this.click}>Click Me</button>
</div>
)
}
}
hooks를 사용한 함수 컴포넌트
import React from 'react';
export default function FunctionalComponent() {
const [state, setState] = React.useState({ count: 0 });
function click() {
setState(state => {
return {
count: state.count + 1
}
});
}
return (
<div>
<p>You Clicked {state.count} times</p>
<button onClick={click}>Click Me</button>
</div>
)
}
- React.useState : 인자에값을 넣으면, [ 인자로 전달한 state 값, Function ] 형태의 배열을 반환하고, 이는 state와 setState에 구조분해할당됩니다. 반환된 배열의 값 중 함수는 state의 값을 변경할 수 있는 함수입니다.
- React.useEffect : 클래스 컴포넌트의 componentDidMount, componentDidUpdate와 비슷한 동작을 하는 함수입니다. 화면이 렌더링 되면, useEffect에 인자로 넣은 함수가 실행됩니다.
- useEffect 함수가 함수를 반환하면, 컴포넌트가 Unmount 될 때, 반환된 함수가 실행됩니다.
-
React.useEffect(() => { document.title = `hello`; return function cleanup() { // cleanup } });
- useEffect는 렌더링 후에 항상 실행되기 때문에, 성능에 영향을 끼칠 수 있습니다. 따라서, Effect의 실행을 건너뛰어야 하는 경우엔 아래처럼 배열을 인자로 전달하면 됩니다.
-
React.useEffect(() => { document.title = `You Clicked ${count} times`; }, [count]); // count가 바뀔 때만 Effect를 재실행합니다.
- 이외에도 useReducer, useMemo, useCallback 등의 다양한 hook을 지원합니다.
- click() : 버튼이 클릭되면 이전에 할당한 setState 함수로 state의 값을 변경해주고 있습니다.
2. Context
- React에서의 데이터 전달은 위에서 아래로 흐르지만, 간혹 여러 컴포넌트에 데이터를 전달해줘야 하는 경우가 있을 수 있습니다.
- context를 이용하면, 이러한 데이터를 컴포넌트끼리 공유할 수 있도록 할 수 있습니다.
일반적인 데이터 흐름( 위 -> 아래 )
import React, { useState } from 'react';
export default function A() {
const [value, setValue] = useState('not changed');
return (
<>
여긴 A
<B value={value} />
<button onClick={click}>C의 값 바꾸기</button>
</>
);
function click() {
setValue("C 값을 변경");
}
}
function B({ value }) {
return (
<div>
<p>여긴 B</p>
<C value={value} />
</div>
);
}
function C({ value }) {
return (
<div>
<p>{value}</p>
</div>
);
}
반대로, 아래에서 위로 데이터 전달
- 함수를 setValue 함수를 하위 컴포넌트로 전달해서 변경해줄 수 있습니다.
import React, { useState } from 'react';
export default function A() {
const [value, setValue] = useState('not changed');
return (
<>
<p>{value}</p>
<B setValue={setValue} />
</>
);
}
function B({ setValue }) {
return (
<div>
<p>여긴 B</p>
<C setValue={setValue} />
</div>
);
}
function C({ setValue }) {
return (
<div>
<p>여긴 C</p>
<button onClick={click}>A의 데이터 변경</button>
</div>
);
function click() {
setValue('A의 데이터가 변경됨');
}
}
위와 같은 방법은 여러 컴포넌트들을 거쳐서 전달되기 때문에, 위험을 초래할 수 있을 것입니다.
이번엔 React의 Context API를 이용해서 데이터를 공유하는 방법을 알아보겠습니다.
Context API를 활용한 데이터 공유
/* PersonContext.jsx */
import React, { createContext } from 'react';
const PersonContext = createContext();
export default PersonContext;
- createContext() : Context 객체를 만듭니다. 이 Context를 구독하고 있는 컴포넌트끼리 데이터가 공유됩니다. 적절한 Provider를 찾지 못하면 defaultValue를 사용하도록 인자를 넣을 수 있습니다.
Example.jsx
/* Example.jsx */
import { useContext } from 'react';
import PersonContext from '../contexts/PersonContext';
export default function Example() {
const persons = useContext(PersonContext);
return (
<ul>
{persons.map(person =>
<li key={person.id}>{person.name}</li>
)}
</ul>
);
}
- useContext : Context 객체를 받아서 그 Context의 현재 값을 반환합니다. 가장 가까운 Provider가 갱신되면 가장 최신의 Context Value를 사용해 화면을 다시 렌더링합니다.
App.js
/* App.js */
...
import Example from './components/Example';
import PersonContext from "./contexts/PersonContext";
const persons = [
{ id: 0, name: 'Mark', age: 39 },
{ id: 1, name: 'Hanna', age: 28 }
];
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<PersonContext.Provider value={persons}>
<Example />
</PersonContext.Provider>
</header>
</div>
);
}
export default App;
- PersonContext.Provider : Context 객체에 포함된 컴포넌트입니다. 이 Context를 구독하고 있는 컴포넌트들에게 Context의 변화를 알리는 역할을 합니다. 이 코드에선 App 컴포넌트와 그 하위 컴포넌트들에게 Context의 변화를 알려주게 됩니다.
전체적인 절차
- PersonContext.jsx 파일을 Context 객체로 생성
- PersonContext.Provider 컴포넌트로 Context의 범위를 지정
- PersonContext를 구독하는 각 컴포넌트에서 useContext를 사용하여 공유 데이터 사용
'JavaScript > React' 카테고리의 다른 글
React - useMemo vs. useCallback (0) | 2022.03.14 |
---|---|
React - 배포하기 (0) | 2022.02.23 |
React - ControlledComponent, UnControlledComponent (0) | 2022.02.07 |
React - Component Styling (0) | 2022.01.28 |
React - React Router DOM (0) | 2022.01.26 |