웹/React

[React] context api 사용시 주의할 점(vs. recoil)

paran21 2023. 8. 31. 13:41

이전 포스팅에서 간단한 Todo web app을 context api를 사용해서 만들어보았다.

 

context api를 사용하면서 여러 컴포넌트에서 상태를 공유해야할 때 간단히 쓸 수 있는 방법이지만, 관리해야하는 상태가 많아지만 Provider depth가 굉장히 증가해서 관리하기 어려울 거라고 느꼈다.

한편으로 최근 많이 사용한다는 Recoil이 궁금해서 찾아보던 중, context api와 recoil을 비교하는 글을 다수 접할 수 있다.

특히 context api에 대해 공통적으로 언급되는 주의사항이 있었는데, 바로 모든 자식 컴포넌트가 리렌더링된다는 점이다.

그래서 React 공식문서를 다시 찾아봤다.

 

공식문서에 따르면, 다음과 같이 설명하고 있다.

... Whenever MyApp re-renders (for example, on a route update), this will be a different object pointing at a different function, so React will also have to re-render all components deep in the tree that call useContext(AuthContext).

In smaller apps, this is not a problem. However, there is no need to re-render them ... has not changed. To help React take advantage of that fact, you may wrap the login function with useCallback and wrap the object creation into useMemo. ...

그래서 정말로 모든 컴포넌트가 리렌더링되는지 확인해보았다.

 

확인방법은 React dev tool의 highlight 기능을 이용하였다.

 

1. Context Api를 사용하는 경우

https://github.com/paran22/simple_todo

TodoListProvider의 하위 컴포넌트로는 Header, TodoList, Footer가 있다.

만약에, Footer에서 새로운 todo를 추가하면 TodoList(새로운 데이터 추가), Footer(input 변경)만 리렌더링되면 된다.

그런데 실제로는 Header, TodoList, Footer가 모두 리렌더링되는 것을 확인할 수 있었다.

2. Recoil을 사용하는 경우

https://github.com/paran22/simple_todo/tree/with-recoil

그렇다면 Recoil을 사용하면 어떨까?

TodoList를 Context api로 관리하는 대신, Recoil을 이용해 TodoListState를 만들고, useTodoList로 로직을 관리하도록 수정하였다.

export const todoListState = atom(
  {
    key: "todoListState",
    default: selector({
      key: "todoListStateSelector",
      get: getSavedData,
    }),
  }
);
export default function useTodoList() {
  const [todoList, setTodoList] = useRecoilState(todoListState);
  ...
  const checkItem = (id) => {
  ...
  };
  const deleteItem = (id) => {
  ...
  };
  const addItem = (text) => {
  ...
  };
  return { todoList, checkItem, deleteItem, addItem }
}

그리고 Footer에 새로운 todo를 추가하니 TodoList(todoListState update)와 Footer(input update)만 리렌더링되는 것을 확인할 수 있었다.

또, Recoil을 사용하면 Context api마다 Provider를 추가해주지 않아도되서 depth가 깊어지지 않고, 사용할 때도 React의 useState와 유사해서 편했다.

 

다크/라이트 모드와 같이 앱 전체가 리렌더링되어야 하는 경우나, 상태변경이 많지 않고 간단한 앱이라면 Context api로도 충분하지만, 상태변경이 잦다면 Recoil과 같이 다른 상태관리 라이브러리를 고려하는 게 좋을 것 같다.