본문 바로가기
React

[리액트] useContext 정리

by 메이플 🍁 2022. 8. 22.

useContext란?

특정한 값을 모든 컴포넌트에서 사용할 수 있도록 만들어놓고, 어디서든 만든 값을 불러와서 사용할 수 있도록 해주는 Hook

 

useContext의 장점

useContext가 없다면 상위 컴포넌트에서 생성한 데이터를 하위 컴포넌트에 전달하기 위해 해당 데이터가 필요하지 않은 여러 컴포넌트를 거치면서 props drilling 현상이 생긴다. useContext를 사용하면 해당 데이터가 필요한 부분에서만 값을 호출해 사용할 수 있다.

 

하지만 그렇다고 해서 항상 useContext 를 써야하는건 아니다. useContext는 여러 하위 컴포넌트에서 공유되어야 하는 값이 있을 때만 사용하는 것이고, 각 컴포넌트로별로만 관리하는 값이라면 그냥 useState 를 쓰는 것이 더 적절하다.

 

useContext의 단점

리액트는 state 값이 변하면 해당 state가 있는 컴포넌트를 다시 렌더링 한다. 하지만 context를 사용해 state값을 사용하게 되면 해당 state를 가지고 있지는 않아도 useProvider의 모든 하위 컴포넌트가 전부 다 렌더링이 된다. 즉 렌더링 시간이 훨씬 길어지게 된다.

useContext 사용법

1. createContext 함수로 context 생성

const UserContext = createContext()

2. 생성한 context로 컴포넌트를 감싸주고 value에 하위 컴포넌트가 사용할 값을 전달

UserContext로 감싸진 UserList 컴포넌트와 UserList 컴포넌트의 모든 하위 컴포넌트들은 count 데이터를 쉽게 전달 받을 수 있다.

<UserContext.Provider value={ count: 1 }>
  <UserList />
</UserContext.Provider>

3. 만들어 놓은 context를 불러온 후에 useContext 를 써주면 Provider의 value 부분을 통해서 넘겨준 그 값을 받아올 수 있게 된다

import { UserContext } from '../App'
const { count } = useContext(UserContext)

 

예제 1

App.jsx

가장 최상위 컴포넌트인 App에서 useContext 훅을 사용해서 App의 모든 하위 컴포넌트가 state와 dispatch 데이터에 접근이 가능하도록 한다. 이때 만든 Context는 컴포넌트 밖에서 정의한 후 export해 하위 컴포넌트가 import가 가능하도록 한다.

import React, { createContext, useReducer } from 'react'
import { userData } from './constant/userData'
import { userReducer } from './reducers/userReducer'
import UserList from './components/UserList'

export const UserContext = createContext()

const App = () => {
  // const [state, dispatch] = useReducer(리듀서 이름, 초기 데이터)
  const [state, dispatch] = useReducer(userReducer, userData)

  return (
    <UserContext.Provider value={{ state, dispatch }}>
      <UserList />
    </UserContext.Provider>
  )
}

export default App

UserList.jsx

import React, { useContext, useRef, useState } from 'react'
import { UserContext } from '../App'

const UserList = () => {
  const [userInput, setUserInput] = useState({
    id: '',
    name: '',
    email: '',
  })

  const userInputHandler = (e) => {
    const { name, value } = e.target
    setUserInput({
      ...userInput,
      [name]: value,
    })
  }

  const userId = useRef(11)

  const { state, dispatch } = useContext(UserContext)

  const addUser = (userInput) => {
    dispatch({
      type: 'ADD',
      data: { ...userInput, id: userId.current },
    })
    userId.current += 1
  }

  const removeUser = (userId) => {
    dispatch({
      type: 'REMOVE',
      data: { id: userId },
    })
  }

  return (
    <>
      <div>
        {state.map((user) => (
          <div key={user.id}>
            <p>{user.name}</p>
            <button onClick={() => removeUser(user.id)}>제거하기</button>
          </div>
        ))}
      </div>
      <input
        type="text"
        name="name"
        placeholder="name"
        onChange={userInputHandler}
      />
      <input
        type="email"
        name="email"
        placeholder="email"
        onChange={userInputHandler}
      />
      <button onClick={() => addUser(userInput)}>이름 추가</button>
    </>
  )
}

export default UserList

 

예제 2

context 관련 로직 컴포넌트에서 사용하지 않고 따로 빼주기

UserContext.js

import React, { createContext, useReducer } from 'react'
import { userData } from '../constant/userData'
import { userReducer } from '../reducers/userReducer'

export const UserContext = createContext()

export function UserProvider({ children }) {
  const [state, dispatch] = useReducer(userReducer, userData)

  return (
    <UserContext.Provider value={{ state, dispatch }}>
      {children}
    </UserContext.Provider>
  )
}

App.jsx

import React from 'react'
import { UserProvider } from './contexts/UserContext'
import UserList from './components/UserList'

const App = () => {
  return (
    <UserProvider>
      <UserList />
    </UserProvider>
  )
}

export default App

UserList.jsx

import React, { useContext, useRef, useState } from 'react'
import { UserContext } from '../contexts/UserContext'

const UserList = () => {
  const [userInput, setUserInput] = useState({
    id: '',
    name: '',
    email: '',
  })

  const userInputHandler = (e) => {
    const { name, value } = e.target
    setUserInput({
      ...userInput,
      [name]: value,
    })
  }

  const userId = useRef(11)

  const { state, dispatch } = useContext(UserContext)

  const addUser = (userInput) => {
    dispatch({
      type: 'ADD',
      data: { ...userInput, id: userId.current },
    })
    userId.current += 1
  }

  const removeUser = (userId) => {
    dispatch({
      type: 'REMOVE',
      data: { id: userId },
    })
  }

  return (
    <>
      <div>
        {state.map((user) => (
          <div key={user.id}>
            <p>{user.name}</p>
            <button onClick={() => removeUser(user.id)}>제거하기</button>
          </div>
        ))}
      </div>
      <input
        type="text"
        name="name"
        placeholder="name"
        onChange={userInputHandler}
      />
      <input
        type="email"
        name="email"
        placeholder="email"
        onChange={userInputHandler}
      />
      <button onClick={() => addUser(userInput)}>이름 추가</button>
    </>
  )
}

export default UserList

댓글