React.js context API

강정우·2023년 1월 3일
0

react.js

목록 보기
18/45
post-thumbnail
post-custom-banner

context API

문제

  • 앞서 우리는 props를 필요한 컴포넌트간 direct로 연결되는 건 불가능하다고 했다.
    그러면 프로젝트규모가 커지면 몇가지 문제점이 생기는데 너무 많은 불필요한 함수들이 생긴다는 것이다. 단지 data를 옮기려고.

  • 그래서 실제로 필요한 데이터를 부모로부터 받는 컴포넌트에서만 사용할 수 있으면 더 좋을 것이다. 부모를 통해 데이터를 전달하지 않도록, 부모는 데이터를 관리하지도 필요하지도 않으니까.

  • 이를 위해 컴포넌트 전체에서 사용할 수 있는, 리액트에 내장된, 내부적인 state 저장소가 있다.
    그것이 바로 리액트 컨텍스트라는 개념이다.

공급(provider)

  • prop을 저장할 store을 생성 후 밑에 적절한 prop이름으로 js파일 생성.
  • 위에서 생성한 파일을 컴포넌트로 가져와서 위에 auth-context.js가 필요한 모든 tag를 감싸주면 된다.
  • 저것 자체는 컴포넌트가 될 수 없다. 하지만 . 으로 컴포넌트를 포함하는 해당 객체의 속성에 접근할 수 있다. 그것이 바로 provider이다. 쉽게말해 바로 얘가 컴포넌트로 바꿔주는 속성이다.
  • 하지만 기본값(pros)이 정의되어있다면 provider는 필요가 없다. error만 야기할 뿐이다. 그런데 사실 provider를 안 쓴다면 굳이 store파일을 만들어서 추가 컴포넌트를 어거지로 만들필요는 없다.
  • 따라서 Fragment 태그도 지워도 된다.
  • 또한 consumer가 접근할 수 있도록 value prop으로 보내준다.
  • 이렇게 해야 이제 이 값이 바뀔때마다, 여기 이 state를 업데이트 할 때마다 결과적으로 이 컴포넌트는 재구성된다.
    그럴때마다 Provider가 새로운 값을 가지게 되고 그러면 Provider를 따르는 모든 자식들도 그 새로운 값을 가지게 되는 것이다.

응용 (공급)

export const 컴포넌트명 = React.createContext({
    초기키값: 초기밸류(배열, 객체 아무거나)
});

export default props => {
    const [스테이트값, set스테이트] = useState([{
        객체1
    },
    {
        객체2
    },
    {
        객체3
    },
    {
        객체4
    }])

    return (
        <컴포넌트명.Provider value={{ 키캆: 스테이트값 }}>
            {props.children}
        </컴포넌트명.Provider >
    );
};

소비(consumer)

provider, consumer를 이용하여 listen

  • 일반적으로 리액트 훅을 이용하지만 consumer를 사용하여 소모할 수 있다.
  • Consumer는 일반적으로 자식을 갖고있는 이는 함수여야한다. 하지만 함수의 return값을 JSX코드를 반환하는 건 좋지 않기 때문에 훅을 이용하여 바꿔보겠다.

context hook을 이용하여 listen

  • 위 코드보다 더 깔끔하고 엘레강스하다.

응용 (소비)

import 아무이름Provider from "해당경로"

     .. 로직 ..

return (
  <아무이름Provider><다양한_컴포넌트></아무이름Provider>
)
  • 이때 아무이름Provider는 위의 응용 (공급) 에서 export default로 무명으로 지어져 있어야 아무이름이 가능하다.

꿀팁(IDE)

  • 현재 우리는 context를 이용하여 자동완성으로 만들어보려했는데 IDE는 logout의 함수가 있는지 알 수 없다. 그래서 우리가 알려주자
  • 약간 선언 느낌으로
  • 그러면 이제 logout 메서드가 생긴것을 볼 수 있다.

임무가 분산되고 state를 집중화하기

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 storedUser = localStorage.getItem("isLoggedIn");
        if (storedUser === "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;
  • 위와 같이 각 컴포넌트에서 쓰일 login과 authenticate 객체들을 한곳에 모아두고
  • 곳곳에서 사용할 수 있다.
    다만 프로젝트 전반에서 사용하려면 root 컴포넌트인 App.js를 index.js에서 감싸줘야한다.

한계 및 주의점

한계

  • state 변경이 잦은 경우에는, 리액트 컨텍스트는 그다지 적합하지 않다.
    예를 들어, 매초 또는 1초에 여러 번 state가 변경되는 경우이다.

  • 로그인 같은 경우에는 1초에 여러번 바뀔일이 없기 때문에 이런 경우는 컨텍스트를 사용하면 좋다.
    그러나 state가 훨씬 더 자주 바뀌는 경우에는 리액트 컨텍스트는 이에 최적화되어 있지 않다.

  • 리액트의 context API가 작동하는 방식이 context에서 무언가 변경되었을 때 이 컨텍스트를 사용하는 어떤 컴포넌트가 관련이 있는 건지 어떤 컴포넌트가 관련이 없는 건지 알아내는 똑부러진 방법을 가지고 있지 않는다.

  • 그 말인 즉, 컨텍스트 안의 어떤 항목을 변경하게 되면 직접적으로 영향을 받았는지 여부와는 상관 없이 useContext를 사용하는 모든 컴포넌트가 다시 빌드 되고, 다시 렌더링 된다는 것이다.

  • 그리고 앞서 언급했듯 리액트의 context API는 일반적으로 최적화되어 있지 않는다.
    앱의 전역적 state 관리 도구로 쓰이도록 만들어지지 않았다는 말이다.
    인증 상태나 테마와 같은 일부 state를 위해 만들어졌다.

  • 이 경우엔 리덕스를 사용하면 되고 포스팅을 확인하면 된다.

    하지만 역시 앞서 언급했듯 만약 props chain이 매우 길어질 경우에는 아주 유용하게 사용될 수 있다.

훅의 규칙(주의점)


1. 리액트 훅은 리액트 함수에서만 호출해야 한다. 즉, 컴포넌트 함수에서 또는 사용자 훅에서
2. 리액트 훅은 리액트 컴포넌트 함수 또는 사용자 정의 훅 함수의 최상위 수준에서만 호출해야 한다
예를 들어 중첩 함수(혹은 콜백함수)에서 훅을 호출하지 말고, block 문에서 호출하지 마라는 뜻이다.

profile
智(지)! 德(덕)! 體(체)!
post-custom-banner

0개의 댓글