본문 바로가기
  • 삽질하는 자의 블로그
React/React-Basic

5. Context 다뤄보기 - 기본편

by 이게뭐당가 2022. 12. 8.

Context 를 사용해보자

 

Context : 앱 전반에 걸쳐 같은 데이터를 공유하게 만들어 주는 HOOK

 

Context 의 기본 구성 요소

"1) createContext"
        :   createContext는, context 를 만들어주는 react의 빌트인 메서드이다.

        [const myContext = createContext(defaultValue)]
                    
            * defaultValue는, "자동완성" 을 위해 사용한다고 해도 무방하다.
                Provider 를 통해, "아무값도 넘기지 않으면", defaultValue 가 사용된다.

"2) context.Provider"

        : Conetext.Provider 는, "context 를 구독하는 컴포넌트"들에게, "context 의 변화를 알린다".

        [myContext.Provider value={값}]

 

Context 의 기본 사용

1) "context" 를 만든다.
2) "context를 공급할 곳" 에서, "만든 context를 import" 한다.
3) App.js 처럼, "전방위적으로 앱을 감싸는 컴포넌트"를 "context 로 감싼다."
4) 단 감쌀때는, "context 변화를 알리는, context.Provider" 로 "감싸야"한다.  [공급한다.]

        
       <context>
               import { createContext } from "react";
               const AuthContext = createContext({
                    isLoggedIn: false,
                });
               export default AuthContext;
               
       <App.js>

               import AuthContext from "../store/..."
                    ...
                    <div className="App">
                       <AuthContext.Provider value={ {isLoggedIn: false} }>
                            <Login />
                            <NewExpenseon SaveChildData={saveExpenseDataHandler} />
                            <Expense fromNewExpense={expensData} yearValue={year} />
                       </AuthContext.Provider>
                    </div>

     "기본 값 (defaultValue) 는 공급자가 없을때나, 유용한 것"이다. "Provider 가 전달하는 value 와는 전혀 관계 없다". 다만, "자동완성기능"을 위해 필요하다
     Provder 는 value 를 하위 항목들에게 "공급"한다. "공급된 value" 는, "하위항목들도 접근하여 사용이 가능"하다.
     지금은  <AuthContext.Provider value={ {isLoggedIn: false} }> 처럼 고정된 value 를 공급하지만, 
              App.js 에서 "State" 를 사용해, "동적으로 공급" 할 수도 있다.

 

 

useContext Hook 을 이용하여 사용(구독)하기

1) useContext 를 import 한다.
2) store 에 담겨있는 context 객체를 import 한다.
3) "useContext" 를 이용하여, "context를 구독"한다
4) context 를 사용한다.

         < Login Component >
            
                import useContext from "react"
                import AuthContext from "../store/.."

                function Login(){
                   const ctx = useContext(AuthContext)

                    retrun(
                        <div>
                           {ctx.isLoggedIn}
                        </div>
                    )
                }

 

 

[헷갈리는것, 반드시 알아야 할 것!]

 ** 컨텍스트를 Provide 하는 "Provider는", "context 에 있는 isLoggedIn 을 전달 하는 것이 아니다."
            스스로 적은 "value" 객체를 전달하는 것이다.

  		"이 말인 즉, 굳이 store 폴더에서, defaultValue 를 적지 않아도, "
                Provider의 value 를 통해, "아무 값(혹은 함수)이나 추가하여 전달 할 수 있다는 것"이다.
                    [context 에서 정의하지 않아서, 자동완성은 안되겠지만]

 ** 이는 보통, "context의 상태(데이터)를 변화하는 함수들을 추가하여 전달" 할것이다. 
                

            <App.js>

                import AuthContext from "../store/..."
                    const [isLogin, setIsLogin] = useState(false)

                    ...
                    function changeState (...){
                        setIsLogin(...)
                    }

                    <div className="App">
                        <AuthContext.Provider value={ {isLoggedIn: isLogin, onClick={changeState}} }>  
                            <Login />
                            <NewExpenseon SaveChildData={saveExpenseDataHandler} />
                            <Expense fromNewExpense={expensData} yearValue={year} />
                        </AuthContext.Provider>
                    </div>

** 사용하는 곳에서 "useContext" 를 사용하여, "함수를 받아 사용"한다면, "App.js" 에서 정의된 함수이므로, 
     App.js 의 "isLogin State" 가 변하고, 이는 "공급할 isLoggedIn 의 value 가 바뀔것"이고
     "이는 구독하여 사용하는 모든 곳에 있는  value 가 바뀔것"이다.

 

store 의 Context 안에서, 모든 공급이 이루어 지도록, 조정하도록 하자

공급하는 곳에, 중구난방 코드를 적지 않기 위하여 리팩토링 하는 것이다.

 

 

1. 기본 사용 메커니즘

        1. store에 context를 만들어, App.js 에 .Proivder 를 이용해, 하위 컴포넌트들을 감싸, value 를 넘긴다.

        2. value 는, App.js 의 .Proivder 안에서 정의되며, App.js의 함수와 State를 모든 앱에 걸쳐 공급한다.

        3. App.js 에서 정의된 함수와 State 를 넘겨받은 하위 컴포넌트들은,
                                           useState 를 사용하여, vlaue 에 접근해, value 를 사용한다.

       value 에 넣어 공급할, State와, 함수들을 App.js 에서 정의하기에, 복잡해진다.

 

2. 리팩토링한 메커니즘

        1.  AuthContext 를 Provide 하기위한 함수를 재정의 [AuthContextProvider]

         2. 재정의한 AuthContextProvider 를 export 하고, 반환값으로, AuthContext.Proivder 태그 안에,
                       props.children 을 사용해 Provider 태그로 모든 하위 컴포넌트를 감싼다. = 공급한다.

         3. value = { {name: ..., login: ...} } 처럼 각각의 객체를 정의하지 말고,
                  context 라는 변수를 만들어, value 안에 넣어준다. 실질적으로 공급되는 값들은  context 인것이다.
               
          4. context 안에는, "State" 와  "함수(State를 조작할)" 가 들어간다. (반드시 그럴 필요는 없다)

          5. createContext 로 만든 AuthContext 안에는 defaultValue 가 들어간다.
                    defaultValue 는 단지, "자동완성"을 위한 값일 뿐이다.
                        어차피 Proivder를 사용한다면, defaultValue의 데이터는 필요없다.
<store/AuthContext.js> ================================================================================

       import { createContext, useState } from "react";

       const AuthContext = createContext({
           isLoggedIn: false,
           loginHandler: ()=>{},
           logoutHandler:()=>{}
       });

       export function AuthContextProvider(props) {
           const [isLoggedIn, setIsLoggedIn] = useState(false);

           function loginHandler() {
                setIsLoggedIn(true);
           }
           function logoutHandler() {
                setIsLoggedIn(false);
           }

           const context = {
                isLoggedIn: isLoggedIn,
                loginHandler: loginHandler,
                logoutHandler: logoutHandler,
           };

           return (
                <AuthContext.Provider value={context}>
                    {props.children}
                </AuthContext.Provider>
           	);
           }

       export default AuthContext;


< App.js > ================================================================================

    : 기존에" AuthContext를 이용해, .Proivder 메서드로, 공급했다".
          하지만 그 일은, 이제, "AuthContextProvider" 가 대신할것이다.
          또한, App.js 에서 Provider 을 사용할 필요가 없어졌다.

        [전부 모이는 "index.js" 에 가서, "AuthContextProvider" 로 감싸준다.]

< index.js > ================================================================================

       import { AuthContextProvider } from "./store/auth-context";

       const root = ReactDOM.createRoot(document.getElementById("root"));
       root.render(
           <AuthContextProvider>
                <Layout>
                <App />
                </Layout>
           </AuthContextProvider>
       );

< 사용할 곳 > ================================================================================

   : 이제 사용할 곳에서 똑같이, "createContext로 만든, AuthContext 를 import" 하고, 
      "useContext" 를 이용하여, "Provider 내부에 있는 값을 사용"하면된다.

       import { useContext, useState } from "react";
       import AuthContext from "../../store/auth-context";

       function UseContextPractice() {
           const ctx = useContext(AuthContext);

           function login() {
                ctx.loginHandler();       // true
           }
           function logout() {
                ctx.logoutHandler();
           }
           console.log(ctx.isLoggedIn);       // false

           return (
                <div>
                	<button onClick={login}> 컨텍스트 로그인 </button>
                	<button onClick={logout}> 컨텍스트 로그아웃</button>
                </div>
           );
       }

       export default UseContextPractice;

 

Context 를 사용할 때 주의사항

         
            반드시 "매우매우 긴 props chain" 과 "전반적인 앱을 컨트롤하는 State"를 이용할때 (Auth, 전체 Banner 등..) 만 사용한다.
            "자주 바뀌는 State" 에 관해서는 "Context" 를 사용하지 않는다! - 리액트 개발자의 공식문서
            그래서 "Redux" 를 사용할것이다.

댓글