Redux
는 여러 컴포넌트 또는 전체 컴포넌트들의 데이터 상태(state
)를 관리하는 상태 관리 라이브러리 입니다.
props
문법이 귀찮을 때 사용합니다.
React
에서는 하위 컴포넌트가 부모 컴포넌트의 변수값을 사용할려면 반드시 부모 컴포넌트에서 하위 컴포넌트로 사용할 변수를props
형태로 넘겨줘야 합니다.- 만약 하위 컴포넌트가 1개가 아니라 여러개일 경우
Redux
를 사용하면 하위 컴포넌트들에서 쉽게 부모 컴포넌트의 변수를 사용할 수 있습니다.
state
변경 관리할 때 사용합니다.
Redux
를 설치하면state
를 보관할 수 있는 파일을 만들수 있습니다. (ex>store.js
)- 모든 컴포넌트들이
props
없이도store.js
에 있는 변수들을 참조하여 사용할 수 있습니다.
Redux 설치
npm install react-redux
npm install redux
Redux 기본 셋팅
- Store.js
import { Provider, useDispatch, useSelector } from "react-redux" import { createStore } from "redux" const 체중 = 100; // state 맘대로 보관 가능 function reducer(state = 체중, action) { return state; } let store = createStore(reducer) ReactDOM.render( <Provider store={store}> <App /> </Provider> )
Component
에서Store
에 있던state
사용 방법
import { useSelector, useDispatch } from "react-redux" function() App() { const 꺼내온값 = useSelector((state) => state); return ( <div> <p>님의 처참한 몸무게: {꺼내온값}</p> </div> ); export default App; }
실행 결과
store.js
에 저장되어 있던 값100
이 꺼내온값을 통해 화면에 출력됩니다.- 이처럼
Redux
를 사용하면 모든Component
가props
없이도state
를 직접 꺼내어 사용할 수 있으며 이것이 바로Redux
를 사용하는 첫번째 이유라고 할 수 있습니다.
- Store.js에서 상태 관리 방법 (Reducer)
state
값의 수정방법들을Store.js
에 있는Reducer
에 정의합니다.- 각각의
Component
에서는 상태값 변경을 하기위해Store.js
에 정의된Reducer
에 수정 요청을 보내야 합니다. (dispatch
이용)
import { Provider, useDispatch, useSelector } from "react-redux" import { createStore } from "redux" const 체중 = 100; // state 맘대로 보관 가능 function reducer(state = 체중, action) { if(action.type === "증가") return state++; else if(action.type === "감소") return state--; else return stats; } let store = createStore(reducer) ReactDOM.render( <Provider store={store}> <App /> </Provider> )
Component에서 state값을 수정 요청하는 방법 (dispatch)
import { useSelector, useDispatch } from "react-redux" function() App() { const 꺼내온값 = useSelector((state) => state); const dispatch = useDispatch() return ( <div> <p>님의 처참한 몸무게: {꺼내온값}</p> <button onClick={() => {dispatch({type: "증가"}) }}>더하기</button> </div> ); export default App; }
실행 결과
- 더하기 버튼을 클릭하면
Component
가dispatch
함수의type
값에 알맞은 값을Store.js
에 수정 요청을 보내면Store.js
에서reducer
에 정의된 조건에 따라 상태 값을 변경 시킵니다.- 이처럼
Redux
를 사용하면Store
에 있던 상태(state
)값을 변경 및 관리할 수 있으며 이것이 바로Redux
를 사용하는 두번째 이유라고 할 수 있습니다.
- navigation.js (Store.js)
import { Provider, useSelector } from "react-redux" import { createStore } from "redux" function reducer(state = IsLogin, action) { // state 상태 변경 if (action.type === "로그인") { state = true console.log(state) // MainPageTrue() return state } else if (action.type === "로그아웃") { state = false console.log(state) LoginTrue() return state } else return state } let store = createStore(reducer) return ( <> <Provider store={store}> <Container> <LoginModal isModal={LoginModalIsOpen} setModal={setLoginModalOpen} /> <MainPageModal isModal={MainPageModalIsOpen} setModal={setMainPageModalOpen} /> <NoticeModal isModal={NoticeModalIsOpen} setModal={setNoticeModalOpen} /> <MyInformationModal isModal={MyInformationModalIsOpen} setModal={setMyInformationModalOpen} /> <ChallengeModal isModal={ChallengeModalIsOpen} setModal={setChallengeModalOpen} /> <BoardModal isModal={BoardModalIsOpen} setModal={setBoardModalOpen} /> <BoardList isModal={BoardListModalIsOpen} setModal={setBoardListModalOpen} /> <PushUpModal isModal={PushUpModalIsOpen} setModal={setPushUpModalOpen} /> <SitupModal isModal={SitUpModalIsOpen} setModal={setSitUpModalOpen} /> <SquatModal isModal={SquatModalIsOpen} setModal={setSquatModalOpen} /> <SignupModal isModal={SignupModalIsOpen} setModal={setSignupModalOpen} /> </Container> </Provider> <> )
- Login.js (Login Component)
import { useDispatch } from "react-redux" // 로그인 상태 변화 (dispatch) const dispatch = useDispatch() // 일반 이메일 로그인 const onSubmit = async (event) => { try { let data // login => 로그인 화면 data = await authService.signInWithEmailAndPassword(ID, PW) window.alert("로그인 성공") dispatch({ type: "로그인" }) console.log(data) } catch (error) { console.log(error) window.alert("이메일과 비밀번호를 다시 확인해주세요") } } // 소셜 로그인 Google & Facebook const auth = getAuth() const user = auth.currentUser const onSocialClick = async (event) => { const { target: { name }, } = event let provider if (name === "google") { provider = new firebaseInstance.auth.GoogleAuthProvider() await authService.signInWithPopup(provider) // MainPageTrue() dispatch({ type: "로그인" }) } else if (name === "facebook") { provider = new firebaseInstance.auth.FacebookAuthProvider() await authService.signInWithPopup(provider) dispatch({ type: "로그인" }) } window.alert("로그인 성공") UserImage = user.photoURL // 프로필 사진 URL UserName = user.displayName // 표시 이름 UserEmail = user.email // 이메일 }
Menubar.js
import { useSelector } from "react-redux" function Menubar() { console.log("Menubar") const selectorData = useSelector((state) => state) const [data, setData] = useState(selectorData) useEffect(() => { setData(selectorData) }, [selectorData]) return ( <> <MenuBar> <Symbol src={Logo} /> <MenuBtn1 onClick={() => { data === true ? MainPageTrue() : LoginTrue() }} 로그인 </MenuBtn1> <MenuBtn2 onClick={() => { NoticeTrue() }} 공지사항 </MenuBtn2> <MenuBtn3 onClick={() => { data === true ? InformationTrue() : window.alert("로그인이 필요한 페이지 입니다.") }} 내정보 </MenuBtn3> <MenuBtn4 onClick={() => { data === true ? ChallengeTrue() : window.alert("로그인이 필요한 페이지 입니다.") }} 챌린지 </MenuBtn4> <MenuBtn5 onClick={() => { data === true ? BoardTrue() : window.alert("로그인이 필요한 페이지 입니다.") }} 소통 게시판 </MenuBtn5> </MenuBar> </> ) } export default Menubar
useEffect
내부에서는 useSelector
를 사용할려고 하니 이러한 에러 메시지가 출력 됐다.Error
React Hook "useSelector" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function
응답 후크 "선택기 사용"은 콜백 내에서 호출할 수 없습니다. 반응 후크는 반응 기능 구성 요소 또는 사용자 지정 반응 후크 기능에서 호출되어야 합니다.
해결 방안
const selectorData = useSelector((state) => state) const [data, setData] = useState(selectorData) useEffect(() => { setData(selectorData) }, [selectorData])
useSelector
함수에서 반환된 상태 값을 변수에 저장하고 저장한 변수값을useState
에 넣고useEffect
함수 내부에서는selectorData
값이 변경될 때마다setData(selectorData)
를 해주면useEffect
함수 내부에서도useSelector
를 사용할 수 있다.- 참고했던 자료: https://stackoverflow.com/questions/68580218/useselector-can-not-be-called-inside-a-callback