[Context-API] Context-API 개념 각인

SeHoony·2021년 9월 28일
1

개념 각인

목록 보기
1/3
post-thumbnail

Redux를 비롯한 상태관리 라이브러리를 공부하면서 처음 Context-API를 접했다. Context-API는 Redux보다 개념이 직관적이고, 기존의 React 개발을 경험한 사람이라면 쉽게 쓸 수 있을 것이라고 생각한다. 하지만 개념이 잘 서있지 않았다면 해당 라이브러리를 쓰고자 할 때 다시 공부해야하는데, 그 시간을 허비하는 게 너무 짜증났다. 그래서 오늘, 이 곳에서 Context-API 개념은 마무리 딱 짓는다.

1. 주요 개념

부연 설명하자면, 주로 쓰는 useState의 경우에는 부모 - 자식 컴포넌트 간 수직적으로 하나하나 props를 넘겨줘야 한다. 따라서 특정 컴포넌트의 깊이가 깊으면 props를 계속 내려다 줘야하므로 귀찮음이 있었다. Context-API는 간단하다. 그 props를 전역(global)로 관리하겠다는 것이다.

여기서 두 가지 개념만 알면 끝이다. Context(저장소), Provider(제공자)이다. 참고 예시는 실행되지 않을테니 참고만 하자!!

1-1. Context : 저장소

이는 Context-API에서 변수나 메서드를 전역적으로 관리하는 저장소의 역할을 한다. context와 관련된 두 가지 메서드만 기억하자

  • createContext()
    context를 관리하는 스크립트 내에서 context를 생성하는 메서드이다.
// TypeScipt로 작성한 프로젝트의 예시
const TodoListContext = createContext<ITodoListContext>({ // 초깃값 작성
    todoList: [],   
    addTodoList: (todo:string):void => {},
    removeTodoList: (index: number):void => {},
    isShowInput : false
});
  • useContext()
    context 내의 state나 method를 가져다 쓰는 스크립 내에서, context 저장소를 가져올 때 쓰는 것이다.
// useContext의 parameter로는 Context 저장소를 직접 넣어준다.
const {todoList, removeTodoList} = useContext<ITodoListContext>(TodoListContext)

1-2. Provider : 제공자

Provider는 Context 저장소를 App.tsx or App.jsx에 제공하는 역할이다. 주로 Provider에서 실제로 사용할 state나 method를 구현해놓고 전역으로 관리한다.


// Provider는 Parameter로 children 즉, 프로젝트 전반의 컴포넌트를 받는다. 따라서 앞서 타입을 지정해준다.
type Props ={
    children : JSX.Element | Array<JSX.Element>
}

// Provider
const TodoListContextProvider = ({children}:Props) =>{

    // 실질적인 state, method 구축
    const [todoList, setTodoList] = useState<Array<string>>(["강세훈", "kangsehoon"]);
    const addTodoList = (todo:string) => {
        const list = [...todoList, todo];
        setTodoList(list);
    }
    const removeTodoList = (index:number) =>{
        let list = [...todoList];
        list.splice(index, 1);
        setTodoList(list);       
    }    
// ... ...
    
// retur부
    return (
        // Context 저장소에 value를 담아서 provider로 children에게 제공해준다. 
        <TodoListContext.Provider value= {{todoList, addTodoList, removeTodoList, isShowInput}}>
            {children}
        </TodoListContext.Provider>
    )
}
export {TodoListContextProvider, TodoListContext}

2. 구현 순서(flow)

Context-API를 사용할 때 이러한 흐름으로 머리에 넣어놓으면 좋을 거 같아 정리했다. 해당 구현 순서는 TypeScript 환경 내에서 Context-API를 어떻게 구현할 지에 대한 고민이다. 잘 참고하셔서 도움되었으면 좋겠습니다.

2-1. 개괄

  1. context(저장소) type 설정(@types - index.d.ts)
  2. context(저장소) 생성 : createContext
  3. provider(제공자) 함수 생성
    3-1. parameter인 children type 생성 후, parameter 적용
    3-2. 실제 state 및 method 구현
    3-3. return부 구현
  4. App.tsx에서 provider 적용
  5. 컴포넌트에서 context 호출 : useContext

2-2. 코드 예시

이 코드들은 필요에 따라 분절하여 기입한 것으로, 따라친다고 프로젝트에서 돌아가지 않을 가능성이 높습니다. 그냥 눈으로 확인하시고 Context-API의 흐름을 가져가시는 게 더 도움될 것입니다.

1) context type 설정

  • 경로 : "src-context-@types-index.d.ts"
    index.d.ts를 쓰면 이 스크립트 내에서 구현한 type을 전역적으로 사용가능하다.
interface ITodoListContext {
  todoList: Array<string>;
  addTodoList: (todo: string) => void;
  removeTodoList: (index: number) => void;
  isShowInput: boolean;
}

2) context(저장소) 생성 : createContext

  • 경로 : "src-context-index.tsx"
const TodoListContext = createContext<ITodoListContext>({
    todoList: [],   
    addTodoList: (todo:string):void => {},
    removeTodoList: (index: number):void => {},
    isShowInput : false
});

3) provider(제공자) 함수 생성

전체적인 provider의 모습부터 보겠다.

type Props ={
    children : JSX.Element | Array<JSX.Element>
}
const TodoListContextProvider = ({children}:Props) =>{

    // 실질적인 state, method 구축
    const [todoList, setTodoList] = useState<Array<string>>(["강세훈", "박규민"]);
    const [isShowInput, setIsShowInput] = useState<boolean>(false);

    const addTodoList = (todo:string) => {
        const list = [...todoList, todo];
        setTodoList(list);
        AsyncStorage.setItem('todoList', JSON.stringify(list));
    }

    const removeTodoList = (index:number) =>{
        let list = [...todoList];
        list.splice(index, 1);
        setTodoList(list);
        AsyncStorage.setItem('todoList', JSON.stringify(list));
    }
    

    const initData = async() =>{
        try {
            const list = await AsyncStorage.getItem('todoList');
            if(list != null){
                setTodoList(JSON.parse(list))
            }
        } catch (error) {
            throw new Error("fuck you")
        }
    }

    useEffect(()=>{
        initData()
    },[])

    return (
        // Context 저장소에 value를 담아서 provider 제공해준다. children에게
        <TodoListContext.Provider value= {{todoList, addTodoList, removeTodoList, isShowInput}}>
            {children}
        </TodoListContext.Provider>
    )
}
export {TodoListContextProvider, TodoListContext}

3-1) parameter인 children type 생성 후, parameter 적용

provider 코드를 잘라 분석하는 것으로 새로운 코드는 아니다. 3-2, 3-3 동일

type Props ={
    children : JSX.Element | Array<JSX.Element> // JSX.Element 또는 Array<JSX.Element>로 컴포넌트를 의미
}
const TodoListContextProvider = ({children}:Props) =>{... ...}

3-2) 실제 state 및 method 구현

// 실질적인 state, method 구축
    const [todoList, setTodoList] = useState<Array<string>>(["강세훈", "박규민"]);
    const [isShowInput, setIsShowInput] = useState<boolean>(false);

    const addTodoList = (todo:string) => {
        const list = [...todoList, todo];
        setTodoList(list);
        AsyncStorage.setItem('todoList', JSON.stringify(list));
    }

    const removeTodoList = (index:number) =>{
        let list = [...todoList];
        list.splice(index, 1);
        setTodoList(list);
        AsyncStorage.setItem('todoList', JSON.stringify(list));
    }
    

    const initData = async() =>{
        try {
            const list = await AsyncStorage.getItem('todoList');
            if(list != null){
                setTodoList(JSON.parse(list))
            }
        } catch (error) {
            throw new Error("fuck you")
        }
    }

    useEffect(()=>{
        initData()
    },[])

3-3) return부 구현

return (
        // Context 저장소에 value를 담아서 provider 제공해준다. children에게
        <TodoListContext.Provider value= {{todoList, addTodoList, removeTodoList, isShowInput}}>
            {children}
        </TodoListContext.Provider>
    )

3-4) export

export {TodoListContextProvider, TodoListContext}

TodoListContextProvider는 이후 App.tsx에 import되어야 하기에 export된다.
TodoListContext는 이후 각 컴포넌트들이 useContext를 쓸 때, 그 파라미터로 context 저장소가 지정되어야 하기에 export 한다.
결론적으로, context 스크립트를 완성하면 provider와 context 저장소는 빼준다고 생각하면 된다.

4) App.tsx에서 provider 적용

import { TodoListContextProvider } from './Context/TodoListContext';

const App =() =>  {
  
  return (
    <TodoListContextProvider>      
      <TodoListView/>   
   </TodoListContextProvider>
  );
};

provider만 import하고 전체 컴포넌트를 TodoListContextProvider 태그로 감싸준다. 이 코드만 한정지어 설명하자면, TodoListView 내에 해당 프로젝트의 모든 컴포넌트들을 포함하고 있기에 TodoListView 컴포넌트를 감싸준 것이다.

5) 컴포넌트에서 context 호출 : useContext

    const {todoList, removeTodoList} = useContext<ITodoListContext>(TodoListContext)

각 컴포넌트 내에서 위에서 보는 것과 같이 context를 받아오면 된다.

3. 결론

이번 스크립트는 코드가 중요하다기 보다, 다음에 Context-API를 구현할 때 머리에서 일어날 흐름들에 대해 나의 식대로 적어보았다. 답은 분명 아닐 것이고 다 나은 방식은 있을 것이다. 하지만 여기서부터 시작해보자!

profile
두 발로 매일 정진하는 두발자, 강세훈입니다. 저는 '두 발'이라는 이 단어를 참 좋아합니다. 이 말이 주는 건강, 정직 그리고 성실의 느낌이 제가 주는 분위기가 되었으면 좋겠습니다.

0개의 댓글