-
[React] Section 11_3Frontend/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