[FE운영진 스터디] Context

꾸Jun·2024년 5월 29일

🦁 멋사 12기

목록 보기
13/16

Context

React Context는 component간의 데이터를 전달하는 또 다른 방법이다. 기존의 Props가 가지고 있던 단점을 해결할 수 있다.

Props Drilling

Props는 부모에서 자식으로만 값을 전달할 수 있었다.

만약 위와 같이 계층구조가 두 단계라면, App에서 ChildB로 데이터를 직접 전달할 수 없다. 어쩔 수 없이 ChildA가 중간 다리 역할을 한다. App -> ChildA -> ChildB로 데이터가 전달된다.

기존에 작성했던 todo-list 앱도 Props를 사용했기 때문에 App -> List -> TodoItem으로 데이터를 전달하는 방식으로 구현했다. 이러하게 직접 데이터를 전달하지 않는 구조는 당연히 좋지 않다.

지금은 중간 다리 역할을 하는 component는 1개라서 괜찮은데, 구조와 로직이 복잡해지면 App -> ......... -> 이러하게 여러 component를 거치는 구조가 될 수 있다. 만약 props로 전달하는 값이 바뀐다면 모든 props를 수정해야하는 일이 발생할 수도 있다.

이런 구조를 Props Drilling이라고 한다.


Context?

Context를 사용하면 이러한 Props Drilling을 해결할 수 있다. Context는 데이터 보관소같은 객체이다. Context를 생성한 다음에 필요한 데이터들을 넣어놓으면, 원하는 component가 직접 가져다가 사용할 수 있다.

Context를 여러개 만들어서 각각의 component가 사용할 수 있게 할 수도 있다.


실습

현재 코드를 보면 App -> List -> TodoItem의 방향으로 Props가 전달되고 있다. 이 상태를 Props Drilling이 발생했다고 볼 수 있다.

Context를 사용해서 Props Drilling을 없애주기 위해 App component에서 createContext를 import하고, 새로운 Context를 생성해준다. Context를 생성할 때 component 밖에서 생성을 해준다. 왜냐하면 Context는 단순히 데이터를 저장해서 사용하게끔 해주면 되는데, component안에서 만들어주면 계속해서 리렌더링되면서 함수를 실행하기 때문이다. 따라서 계속해서 다시 생성해줄 필요가 없기 떄문에 밖에 만들어준다.

Context안에 Provider라는 속성이 있는데, 공급할 데이터를 설정하거나, 데이터를 공급받을 component들을 설정하기 위해서 사용한다.

App component에서 TodoContext.Provider안에 데이터를 담아주면 Editor, List, TodoItem component들이 사용할 수 있게 된다.

TodoContext.Provider로 3개의 component들을 감싸주고, value안에 전달할 데이터들을 넣어주면 아래 component들이 직접 사용할 수 있게 된다. 기존의 component들에 전달하던 Props를 모두 삭제해주고, 이제 context로부터 데이터를 받아온다.

Editor component에서 import TodoContext, useContext를 해주고, 기존에 받아오던 onCreate Props를 지워준다. {onCreate} = useContext(TodoContext)를 사용해서 받아오면 된다. 나머지 List, TodoItem component들도 원하는 데이터를 useContext로 구조분해할당해서 가져오면 된다.


Context 분리하기

useContext를 사용해서 원하는 데이터를 직접 가져와 사용하게 구현을 했다. 기능적으로는 문제가 없는데, 다시 모든 component들이 리렌더링되고 있다.

이 문제가 발생한 이유는 Provider component도 React의 component이기 때문에 App component로부터 제공받는 todos, onCreate, onUpdate, onDelete를 감싸고 있는 객체가 바뀌면 리렌더링이 발생한다.

그런데 우리는 받는 props가 바뀌지 않으면 리렌더링을 발생시키지않도록 memo를 사용했었다. 그러나 리렌더링되는 이유는 새로운 Todo를 생성, 수정, 삭제할 때 App component의 todos가 변경되어 App component가 리렌더링이 되는데, 그때 Provider component에게 전달하는 객체 자체가 다시 생성되기 때문이다.

이 문제는 state로써 변경될 수 있는 것들은 TodoStateContext, 변경되지 않는 값들은 TodoDispatchContext로 분리하면 해결할 수 있다.

App component에서 todos는 TodoStateContext에 저장해서 List에서 사용하고, onCreate, onUpdate, onDelete는 TodoDispatchContext에 저장해서 Editor, TodoItem에서 사용하게 하면 된다. 이렇게 되면 todo가 생성, 수정, 삭제된다면 todos를 가지고 있는 List만 리렌더링되고 나머지 component들은 todos를 가지고 있지 않기 떄문에 리렌더링되지 않을 것이다.

App component에서 TodoStateComponent, TodoDispatchContext 2개를 생성해준다.

2개를 Provider로 todos와 onCreate, onUpdate, onDelete를 value로 갖게 한다. 이때 onCreate, onUpdate, onDelete는 다시 불리지 않도록 useMemo를 지난 번처럼 사용해준다. 그리고 나서는 각각의 component에 필요한 데이터를 가져와서 사용하면 된다.

결과를 보면 변경이된 TodoItem만 리렌더링되는 것을 확인할 수 있다.



사진 및 참고 출처 - 한입 크기로 잘라먹는 리액트

profile
꾸준🐢

0개의 댓글