[React & Context API] 리액트에서 Context API 구현하기

Hyuk·2023년 1월 11일
0

React에서 각기 다른 컴포넌트에서 데이터가 필요하다면 부모 컴포넌트에서 데이터를 관리하고 각 자식 컴포넌트에 props로 데이터를 넘겨줘야 한다. 이때 데이터가 바뀐다면 모든 경로에 데이터를 보수해줌으로써 유지보수가 용이하지 않다.
또한, 가장 상위의 컴포넌트에서 가장 깊숙한 자식 컴포넌트까지 props를 전해줘야 하는 상황이 있다면?
유지보수 뿐만 아니라 가독성 또한 좋지 않아진다.

가령 전역적으로 데이터를 관리하고 그 데이터를 필요한 컴포넌트에서 사용할 수 있도록 하면 편리 하지 않을까?

Context API를 사용하면 이러한 문제점을 해결할 수 있다.

Context

Context는 리액트 내에서 데이터를 전역적으로 관리할 수 있게 해준다. (물론 꼭 전역적일 필요는 없다.)

사용법

Context는 다음과 같이 createContext 라는 함수를 import 해서 사용할 수 있다.

import { createContext } from 'react';

const Context = createContext(기본값);  
// 기본값은 Consumer 컴포넌트가 Provider 컴포넌트를 찾지 못할 때 사용되는 값

자식 컴포넌트에 함수를 넘겨줄 수 있듯이 다음과 같이 함수도 전달할 수 있다.

import { createContext } from 'react';

const Context = createContext({
	name: 'Hyuk',
  	setName: () => {}
});

Provider와 Consumer

Provider 컴포넌트는 Context의 변화를 하위컴포넌트에 알리는 역할을 한다.

참고로 React Native에서 themeProvider 컴포넌트가 있는데 여기서의 Provider와 같다.

다음과 같이 Provider 컴포넌트에 value 속성에 데이터를 지정하면 하위컴포넌트에 데이터를 전달할 수 있다.

<Provider value={}>
	...
</Provider>

Consumer 컴포넌트는 말그대로 소비자로서, 데이터를 가지고 있는 부모컴포넌트 중에서 가장 가까이에 있는 Provider 컴포넌트가 전달하는 데이터를 이용하는 역할을 한다.
이때 중요한 점은 Consumer 컴포넌트의 자식은 함수여야한다는 점과 Cunsumer 컴포넌트는 Provider 컴포넌트의 자식이어야 한다는 점이다.

// 기본 구조
<Provider>
	<Consumer>
  		{value => ...}
  	</Consumer>
</Provider>

Provider 컴포넌트로 Consumer 컴포넌트를 감싸주고나서 value 값을 적지 않는다면 Consumer 컴포넌트에서는 undefined 값이 나오기 때문에 value 값을 Provider 컴포넌트에서 꼭 적어주어야 한다.

// 예시
const UserContext = createContext({ name: 'Hyuk' });
<UserContext.Provider value={{ name: 'Hyuk' }}>
  <UserContext.Consumer>
  	{(value) => 
		<div>
          {value.name}  // 'Hyuk'
        </div>
	}
  </UserContext.Consumer>
</UserContext.Provider>

이때 createContext의 데이터 형식을 Provider 컴포넌트에서도 똑같은 형식의 데이터를 전달해줘야 한다.

컴포넌트화

그럼 이제 Context API를 컴포넌트화 해서 사용할 수 있다.
다음은 유저 정보를 다루는 컴포넌트를 구현해 봤다.

// src/context/User.js
// 유저 정보를 가지는 컴포넌트
import { createContext, useState } from 'react';

const UserContext = createContext({
	name: '',
  	setName: () => {}
});

const UserProvider = ({ children }) => {
  const [name, setName] = useState('Hyuk');
  const value = {name, setName};
  return (
	<UserContext.Provider value={value}>
    	{children}
    </UserContext.Provider>
  )
}

const UserConsumer = UserContext.Consumer;

export { UserProvider, UserConsumer };
export defalut Usercontext;
// src/components/User.js
// 유저 정보를 렌더링 및 변경할 컴포넌트(input 이 있는 컴포넌트)
import { useState } from 'react';
import { UserConsumer } from '../context/User'

const User = () => {
  const [text, setText] = useState('');
  
  return(
	<>
    <UserConsumer>
    	{({name}) => 
			<div>
    			Name: {name}
    		</div>
		}
    </UserConsumer>
    <UserConsumer>
      {({setName}) => 
			<input 
				value={text}
				onChange={(value) => {
                  setName(value), 
                  setText('')
                }}
			/>
		}  
  	</UserConsumer>
    </>
  )
}

export default User;
// src/App.js

import { createContext } from 'react';
import { UserProvider } from './context/User';
import User from './components/User';

const createContext = () => {
	const UserContext = createContext({ name: 'Hyuk' })
    return (
    	<UserProvider>
      		<User />
      	<UserProvider>
    )
}

export default createContext;

useContext

위에서 말한 것처럼 각 컴포넌트에서 데이터를 사용하기 위해선 props 로 주고받는다. 이때 여러 상황에서의 불편함으로 인해 데이터를 전역으로 관리해야 하는 필요성이 생겼다. 그 후 Context APIProvider 컴포넌트와 Consumer 컴포넌트가 유지보수성은 해결해 주었지만 어딘가 모르게 계속 불편한 점이 있었다.

공부하면서도 계속 조금더 편리한 건 없을까? 하던 의문이 계속 생겨났고 useContxt 를 발견했다.

useContext 를 사용하면 더욱 편리하게 Context API를 사용할 수 있다.

Provider 컴포넌트가 전달하는 데이터를 굳이 Consumer 컴포넌트를 이용하지 않더라도 어디서든 useContext로 사용할 수 있다.

const 데이터 = useContext(Context);

그럼 위에서 Provider 컴포넌트와 Consumer 컴포넌트로 다뤘던 컴포넌트를 useContext 로 구현해보자.

// src/component/User.js
import { useState, useContext } from 'react';
import UserContext from '../Context/User';

const User = () => {
  const [name, setName] = useState('');
  const { name, setName } = useContext(UserContext);
  
  return (
  	<>
    	<span>Name: {name}</span>
		<input 
			value={text}
			onChange={(value) => {
              setName(value), 
              setText('')
            }}
		/>
    </>
  )
}

export default User;
profile
프론트엔드 개발자

0개의 댓글