ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React] Section 11_3
    Frontend/React 2023. 11. 4. 21:55

    일반적으로 데이터는 props를 통해 컴포넌트에 전달된다.

    하지만 항상 문제가 되는건 state를 여러 컴포넌트를 통해 전달하는 경우

    -> prop chain

    -> 실제로 필요한 데이터를 부모로부터 받는 컴포넌트에서만 사용할 수 있으면 더 좋을 것 (부모를 통해 데이터를 전달하지 않도록)

     

    -> 컴포넌트 전체에서 사용할 수 있는, 리액트에 내장된 내부적인 state 저장소

    -> React context

     

    React Context

     

    import React from "react";
    
    const AuthContext = React.createContext({
      isLoggedIn: false,
    });
    
    export default AuthContext;

     

    AuthContext는 컴포넌트는 아니지만, 컴포넌트를 포함할 객체이다.

     

    공급 

    Provider

    <AuthContext.Provider>
          <MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
          <main>
            {!isLoggedIn && <Login onLogin={loginHandler} />}
            {isLoggedIn && <Home onLogout={logoutHandler} />}
          </main>
        </AuthContext.Provider>

     

    AuthContext Provider는 컴포넌트이다.

     

    리스닝

     

    AuthContext consumer 또는 리액트 훅을 사용하여 리스닝할 수 있다.

    -> 일반적으로는 리액트 훅 사용

    1.  Consumer

    const Navigation = (props) => {
      return (
        <AuthContext.Consumer>
          {(ctx) => {
            return (
              <nav className={classes.nav}>
                <ul>
                  {ctx.isLoggedIn && (
                    <li>
                      <a href="/">Users</a>
                    </li>
                  )}
                  {ctx.isLoggedIn && (
                    <li>
                      <a href="/">Admin</a>
                    </li>
                  )}
                  {ctx.isLoggedIn && (
                    <li>
                      <button onClick={ctx.onLogout}>Logout</button>
                    </li>
                  )}
                </ul>
              </nav>
            );
          }}
        </AuthContext.Consumer>

     

    -> 오류 발생 이유 : 기본값이 있으면 Provider는 필요없다.

     

     <AuthContext.Provider
          value={{
            isLoggedIn: false,
          }}
        >

     

    -> AuthContext.Provider에 value 속성 추가

     

        <AuthContext.Provider
          value={{
            isLoggedIn: isLoggedIn,
          }}
        >

     

    2. useContext (React hook)

    const ctx = useContext(AuthContext);

     

    팁 

    1. context 디폴트 객체에 state나 function을 넣어 놓으면 IDE에 자동완성이 뜨기 때문에 수월하다.

    2.

    import React, { useState, useEffect } from "react";
    
    const AuthContext = React.createContext({
      isLoggedIn: false,
      onLogout: () => {},
      onLogin: (email, password) => {},
    });
    
    export const AuthContextProvider = (props) => {
      const [isLoggedIn, setIsLoggedIn] = useState(false);
    
      useEffect(() => {
        const storedUserLoggedInInformation = localStorage.getItem("isLoggedIn");
    
        if (storedUserLoggedInInformation === "1") {
          setIsLoggedIn(true);
        }
      }, []);
    
      const logoutHandler = () => {
        localStorage.removeItem("isLoggedIn");
        setIsLoggedIn(false);
      };
      const loginHandler = () => {
        localStorage.setItem("isLoggedIn", "1");
        setIsLoggedIn(true);
      };
    
      return (
        <AuthContext.Provider
          value={{
            isLoggedIn: isLoggedIn,
            onLogout: logoutHandler,
            onLogin: loginHandler,
          }}
        >
          {props.children}
        </AuthContext.Provider>
      );
    };
    export default AuthContext;
    // index.js
    import React from "react";
    import ReactDOM from "react-dom/client";
    
    import "./index.css";
    import App from "./App";
    import { AuthContextProvider } from "./context/auth-context";
    
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      <AuthContextProvider>
        <App />
      </AuthContextProvider>
    );

     

    -> AuthContextProvider 컴포넌트로 App 컴포넌트를 감싸준다.

    import React, { useState, useEffect, useContext } from "react";
    
    import Login from "./components/Login/Login";
    import Home from "./components/Home/Home";
    import MainHeader from "./components/MainHeader/MainHeader";
    import AuthContext from "./context/auth-context";
    
    function App() {
      const ctx = useContext(AuthContext);
      return (
        <>
          <MainHeader onLogout={ctx.logoutHandler} />
          <main>
            {!ctx.isLoggedIn && <Login />}
            {ctx.isLoggedIn && <Home />}
          </main>
        </>
      );
    }
    
    export default App;

    -> 선택사항이지만 앱 컴포넌트가 더 간결해진다.

     

    -> 구성을 하려면 props를 사용 (컴포넌트를 재사용할 용도일 경우)

    -> 컴포넌트 또는 전체 앱에서 state 관리를 하려면 context 사용

     

    -> 변경이 잦은 경우 context는 그다지 적합하지 않다.

     

    -> 앱 전체에 걸쳐 또는 컴포넌트 전체에 걸쳐 state가 자주 변경되는 경우는? 

    -> Redux 사용!!!

     

    Rules of Hooks

    1. 리액트 훅은 함수에서만 호출해야한다. (리액트 함수 컴포넌트  or 커스텀 hooks 함수)

    2. 리액트 훅은 리액트 컴포넌트 함수 또는 사용자 정의 훅 함수의 최상위 수준에서만 호출해야 한다. (중첩 함수 안, block 안 호출 불가)

     

    useEffect 훅 rule

    항상 참조하는 모든 항목을 의존성으로 useEffect 내부에 추가해야 한다!!!

     

    Forward Refs

    useImperativeHandle

    컴포넌트나 컴포넌트 내부에서 오는 기능들을 명령적으로 사용할 수 있게 해준다.

    state props 관리를 통하지 않고 부모 컴포넌트의 state를 통해 컴포넌트를 제어하지 않고 프로그래밍적으로 컴포넌트에서 무언가를 직접 호출하거나 조작해서 사용하게 해준다.

    -> 거의 사용하지는 않음!!!

    import React, { useRef, useImperativeHandle } from "react";
    import classes from "./Input.module.css";
    
    const Input = React.forwardRef((props, ref) => {
      const inputRef = useRef();
    
      const activate = () => {
        inputRef.current.focus();
      };
    
      useImperativeHandle(ref, () => {
        return {
          focus: activate,
        };
      });
      return (
        <div
          className={`${classes.control} ${
            props.isValid === false ? classes.invalid : ""
          }`}
        >
          <label htmlFor={props.id}>{props.label}</label>
          <input
            ref={inputRef}
            type={props.type}
            id={props.id}
            value={props.value}
            onChange={props.onChange}
            onBlur={props.onBlur}
          />
        </div>
      );
    });
    
    export default Input;

     

    -> React.forwardRef 는 여전히 컴포넌트를 반환한다.

    -> Input은 여전히 컴포넌트이지만 ref에 바인딩 될 수 있는 리액트 컴포넌트

     

    // Login.js
     const submitHandler = (event) => {
        event.preventDefault();
        if (formIsValid) {
          authCtx.onLogin(emailState.value, passwordState.value);
        } else if (!emailIsValid) {
          emailInputRef.current.focus();
        } else {
          passwordInputRef.current.focus();
        }
      };

     

    -> 자주 사용하면 안되지만 포커징이나 스크롤링의 경우 유용하게 사용할 수 있다.

    'Frontend > React' 카테고리의 다른 글

    [React] Section 13  (0) 2023.11.09
    [React] Section 12  (0) 2023.11.07
    [React] Section 11_2  (0) 2023.11.04
    [React] section 11_1  (0) 2023.11.02
    [React] section 9, 10  (0) 2023.11.01
Designed by Tistory.