React Redux 연습

Redux는 애플리케이션의 모든 상태를 각 구성 요소에 두는 대신 하나의 전역 위치에 두는 방법을 제공하는 상태 관리 도구입니다.


전제 조건:


  • React 프로젝트 설정 - npx create-react-app app_name
  • 패키지 - npm i redux react-redux @reduxjs/toolkit



  • 사용된 방법:


  • import { combineReducers, createStore, applyMiddleware } from "redux";
  • import { Provider, useSelector, useDispatch } from "react-redux";
  • import { configureStore } from "@reduxjs/toolkit";


  • 1. CombineReducers
  • 여러 리듀서들을 합체해서 하나로 만들어서 매장으로 보낼때 사용합니다.

  • const reducer = combineReducers({
        userData: userReducer,
        ...
        <more here>
    });
    




    2. createStore(더 이상 사용되지 않음)/configureStore
  • 결합된 리듀서를 사용하여 저장소(redux 상태)를 생성하고 작업이 리듀서에 도달하기 전에 수행해야 하는 경우 미들웨어를 적용하는 데 사용됩니다.

  • const store = createStore(reducer, applyMiddleware(thunk, middelware2, middleware3, etc..));    // this is deprecated.
    (OR)
    const store = configureStore({
        reducer,
        middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(middleware1, middleware2, etc...)
    });
    




    3. 공급자
  • 이 구성 요소는 DOM에 추가되는 최종 구성 요소(index.js에서)를 둘러쌉니다.
  • 이를 통해 전체 애플리케이션에서 상점을 사용할 수 있습니다.

  • ReactDOM.render(
        <Provider store={store}>
            <App></App>
        </Provider>,
        document.getElementById("root")
    );
    




    4. 사용 선택기
  • redux 상태에서 상태 데이터에 액세스하는 데 사용됩니다.

  • const App = () => {
        const userData = useSelector((state) => state.userData);    //  where userData is the key name given in the combineReducers.
        const userId = userData.userId;
    
        return (
            <div>
                <h3>User id is {userId}</h3>
            </div>
        );
    };
    




    5. 디스패치 사용
  • 그에 따라 상태 데이터를 수정하는 작업을 전달하는 데 사용됩니다.

  • const App = () => {
        const dispatch = useDispatch();
    
        const updateUserId = (ev) => {
            dispatch(setUserId(ev.target.value));   //  action creator
            dispatch({type: "SET_USER_ID", payload: ev.target.value});  //  regular dispatch
        };
    
        return (
            <div>
                <input type="text" value={userId} onChange={updateUserId} />
            </div>
        );
    };
    



    단계:




    1. 동작 유형 상수를 선언합니다.
    ~/src/state/actionTypes.js
    const actionTypes = {
        user: {
            SET_NEW_USER: "SET_NEW_USER",
            SET_USER_ID: "SET_USER_ID"
        },
        counter: {
            INCREASE_COUNT: "INCREASE_COUNT",
        },
    };
    export default actionTypes;
    




    2. 감속기를 선언합니다.
    ~/src/state/reducers/counterReducer.js
    import actionTypes from "../actionTypes";
    const counterReducer = (state = 0, action = {}) => {
        switch (action.type) {
            case actionTypes.counter.INCREASE_COUNT:
                return state + action.payload.count;
    
            default:
                return state;
        }
    };
    export default counterReducer;
    

    ~/src/state/reducers/userReducer.js
    import actionTypes from "../actionTypes";
    const userReducer = (state = { userId: 1, userData: {} }, action = {}) => {
        switch (action.type) {
            case actionTypes.user.SET_NEW_USER:
                return {
                    ...state,
                    userData: { ...action.payload },
                };
            case actionTypes.user.SET_USER_ID:
                return {
                    ...state,
                    userId: action.payload,
                };
            default:
                return state;
        }
    };
    export default userReducer;
    




    3. 감속기를 결합합니다.
    ~/src/state/reducers/index.js
        import { combineReducers } from "redux";
        import userReducer from "./userReducer";
        import counterReducer from "./counterReducer";
    
        const reducer = combineReducers({
            userData: userReducer,
            countData: counterReducer,
        });
        export default reducer;
    




    4. 결합된 리듀서와 미들웨어가 있는 경우 전달하여 스토어를 생성합니다.
    ~/src/state/store.js
    //  import { createStore, applyMiddleware } from "redux";
    //  import thunk from "redux-thunk";
    import { configureStore } from "@reduxjs/toolkit";
    import reducer from "./reducers/index";
    
    //  createStore is deprecated..
    // const store = createStore(reducer, applyMiddleware(thunk));  
    
    const store = configureStore({
        reducer,
        middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
    });
    
    // To add additional middlewares,
    // middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(middleware1, middleware2, etc...),  //  thunk is inbuilt in @reduxjs/toolkit
    
    export default store;
    




    5. 기능 요구 사항에 따라 작업을 선언합니다.
    ~/src/state/actions/counterActions.js
    import actionTypes from "../actionTypes";
    const increaseCount = (countToIncrease) => {
        return {
            type: actionTypes.counter.INCREASE_COUNT,
            payload: { count: countToIncrease },
        };
    };
    export { increaseCount };
    

    ~/src/state/actions/userActions.js
    import axios from "axios";
    import actionTypes from "../actionTypes";
    const getUser = (userId) => {
        return async (dispatchMethod, getState) => {
            const userResponse = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);
            const userData = await userResponse.data;
            dispatchMethod({ type: actionTypes.user.SET_NEW_USER, payload: userData });
            return userData;
        };
    };
    
    const setUserId = (userId) => {
        return {
            type: actionTypes.user.SET_USER_ID,
            payload: userId,
        };
    };
    
    export { getUser, setUserId };
    




    6. 기능을 구현하고 필요에 따라 작업을 디스패치합니다.
    ~/src/components/counter/Counter.js
    import { useSelector, useDispatch } from "react-redux";
    import { increaseCount } from "./../../state/actions/counterActions";
    
    const Counter = () => {
        const counterData = useSelector((state) => state.counterData);
        const dispatch = useDispatch();
        const handleIncreaseCount = (count = 1) => {
            dispatch(increaseCount(count));
        };
    
        return (
            <div>
                <h2>Counter</h2>
                <h3>{counterData}</h3>
                <button
                    onClick={() => {
                        handleIncreaseCount(1);
                    }}
                    style={{ marginRight: "10px" }}
                >
                    Increase count by 1
                </button>
                <button
                    onClick={() => {
                        handleIncreaseCount(5);
                    }}
                >
                    Increase count by 5
                </button>
            </div>
        );
    };
    export default Counter;
    

    ~/src/App.js
    import { useState } from "react";
    import { useDispatch, useSelector } from "react-redux";
    
    import { getUser, setUserId } from "./state/actions/userActions";
    
    import Profile from "./components/user/Profile";
    import Counter from "./components/counter/Counter";
    
    const App = () => {
        const userData = useSelector((state) => state.userData);
        const userId = userData.userId;
        const [showLoader, setShowLoader] = useState(false);
        const dispatch = useDispatch();
    
        const getUserInfo = () => {
            setShowLoader(true);
            dispatch(getUser(userId))
                .then((res) => {
                    console.log(res);
                })
                .catch((err) => {
                    console.log(err);
                })
                .finally(() => {
                    setShowLoader(false);
                });
        };
    
        const updateUserId = (ev) => {
            dispatch(setUserId(ev.target.value));
        };
    
        return (
            <div>
                <input type="text" value={userId} onChange={updateUserId} />
                <h3>User id is {userId}</h3>
                <button onClick={getUserInfo}>Get user info</button>
                <br />
                {showLoader ? "loading..." : <Profile></Profile>}
                <hr />
                <Counter></Counter>
            </div>
        );
    };
    
    export default App;
    




    7. 공급자로 전체 애플리케이션을 래핑하고 저장소를 소품으로 전달합니다.
    ~/src/index.js
        import ReactDOM from "react-dom";
        import App from "./App";
        import { Provider } from "react-redux";
        import store from "./state/store";
        ReactDOM.render(
            <Provider store={store}>
                <App></App>
            </Provider>,
            document.getElementById("root")
        );
    



    새로고침 시 redux 상태 유지


  • 이렇게 하려면 다음 두 파일을 변경해야 합니다.
  • ~/src/state/store.js
  • ~/src/index.js

  • 단계:




    1. npm i redux-persist

    2. ~/src/state/store.js


        //  NEW LINES START
        import { persistStore, persistReducer } from "redux-persist";
        import storage from "redux-persist/lib/storage";
    
        //  Setup persist config and persist reducer
        const persistConfig = {
            key: "persisted_state_data",
            storage,
        };
        const persistedReducer = persistReducer(persistConfig, reducer);
        //  NEW LINES END
    
        //  UPDATED LINE START
        //  Pass this persisted reducer to the store configurator and disable the serializableCheck.
        const store = configureStore({
            reducer: persistedReducer,
            middleware: (getDefaultMiddleware) => getDefaultMiddleware({
                serializableCheck: false,
            }),
        });
         //  UPDATED LINE END
    
        //  NEW LINE START
        // Create a persisted store along with the normal store and export both.
        const persistor = persistStore(store);
        //  NEW LINE END
    
        //  UPDATED LINE START
        export { persistor, store };
        //  UPDATED LINE END
    




    3. ~/src/index.js


    //  Import persisted store and the regular store.
    import { persistor, store } from "./state/store";
    import { PersistGate } from "redux-persist/integration/react";
    
    //  Within the redux provider wrap the app with the PersistGate component and pass the persistStore.
    ReactDOM.render(
        <Provider store={store}>
            <PersistGate persistor={persistor}>
                <App></App>
            </PersistGate>
        </Provider>,
        document.getElementById("root")
    );
    





    레포 링크: Github


    추신
  • 첫 포스팅 🙏
  • R&D 시간을 절약할 수 있기를 바랍니다.
  • 좋은 웹페이지 즐겨찾기