반응형

설치

yarn add redux react-redux redux-saga axios @reduxjs/toolkit

예제 코드

  • todoReducer.js

    export const TODO_ACTION_TYPE = {
        FETCH_TODO_LIST_REQUEST: 'todo/FETCH_TODO_LIST_REQUEST',
        FETCH_TODO_LIST_SUCCESS: 'todo/FETCH_TODO_LIST_SUCCESS',
        FETCH_TODO_LIST_FAIL: 'todo/FETCH_TODO_LIST_FAIL',
        FETCH_TODO_REQUEST: 'todo/FETCH_TODO_REQUEST',
        FETCH_TODO_SUCCESS: 'todo/FETCH_TODO_SUCCESS',
        FETCH_TODO_FAIL: 'todo/FETCH_TODO_FAIL',
    };
    
    const initialState = {
        isFetching: false,
        todoList: [],
        todo: {},
        error: {},
    };
    
    const todoReducer = (state = initialState, action) => {
        switch (action.type) {
            case TODO_ACTION_TYPE.FETCH_TODO_LIST_REQUEST:
                return { isFetching: true, todoList: [], todo: {}, error: {} };
            case TODO_ACTION_TYPE.FETCH_TODO_LIST_SUCCESS:
                return { ...state, isFetching: false, todoList: action.payload };
            case TODO_ACTION_TYPE.FETCH_TODO_LIST_FAIL:
                return { ...state, isFetching: false, error: action.payload };
            case TODO_ACTION_TYPE.FETCH_TODO_REQUEST:
                return { ...state, isFetching: true, todo: {}, error: {} };
            case TODO_ACTION_TYPE.FETCH_TODO_SUCCESS:
                return { ...state, isFetching: false, todo: action.payload };
            case TODO_ACTION_TYPE.FETCH_TODO_FAIL:
                return { ...state, isFetching: false, error: action.payload };
            default:
                return state;
        }
    };
    
    export default todoReducer;
    
  • todoSaga.js

    import { call, put, select, takeLatest } from 'redux-saga/effects';
    import axios from 'axios';
    import { TODO_ACTION_TYPE } from './todoReducer';
    
    const fetchTodoList = function* () {
        try {
            const response = yield call(() => axios.get(`https://jsonplaceholder.typicode.com/todos`));
    
            yield put({ type: TODO_ACTION_TYPE.FETCH_TODO_LIST_SUCCESS, payload: response.data });
        } catch (error) {
            yield put({ type: TODO_ACTION_TYPE.FETCH_TODO_LIST_FAIL, payload: error });
        }
    };
    
    const fetchTodo = function* (action) {
        const data = action.payload;
        const id = data[0].id;
    
        try {
            const response = yield call(
                (id) => axios.get(`https://jsonplaceholder.typicode.com/todos/${id}`),
                id,
            );
    
            yield put({ type: TODO_ACTION_TYPE.FETCH_TODO_SUCCESS, payload: response.data });
        } catch (error) {
            yield put({ type: TODO_ACTION_TYPE.FETCH_TODO_FAIL, payload: error });
        }
    };
    
    const printTodoState = function* () {
        const state = yield select(({ todoReducer }) => todoReducer);
    
        console.log('printTodoState', state);
    };
    
    const todoSaga = [
        takeLatest(TODO_ACTION_TYPE.FETCH_TODO_LIST_REQUEST, fetchTodoList),
        takeLatest(TODO_ACTION_TYPE.FETCH_TODO_LIST_SUCCESS, fetchTodo),
        takeLatest(TODO_ACTION_TYPE.FETCH_TODO_SUCCESS, printTodoState),
    ];
    
    export default todoSaga;
    
  • todoStore.js

    import createSagaMiddleware from 'redux-saga';
    import { applyMiddleware, combineReducers, createStore } from 'redux';
    import todoReducer from './todoReducer';
    import { all } from 'redux-saga/effects';
    import todoSaga from './todoSaga';
    
    const createTodoStore = () => {
        const sagaMiddleware = createSagaMiddleware();
    
        const store = createStore(
            combineReducers({
                todoReducer,
            }),
            applyMiddleware(sagaMiddleware),
        );
    
        sagaMiddleware.run(function* () {
            yield all([...todoSaga]);
        });
    
        return store;
    };
    
    export default createTodoStore;
    
  • index.js

    import React from 'react';
    import { Provider, useDispatch, useSelector } from 'react-redux';
    import { TODO_ACTION_TYPE } from './todoReducer';
    import createTodoStore from './todoStore';
    
    const store = createTodoStore();
    
    const Container = () => {
        const todo = useSelector(({ todoReducer }) => todoReducer.todo);
        const dispatch = useDispatch();
    
        return (
            <div>
                <button onClick={() => dispatch({ type: TODO_ACTION_TYPE.FETCH_TODO_LIST_REQUEST })}>
                    try fetch
                </button>
                <div>{todo.title}</div>
            </div>
        );
    };
    
    const App = () => {
        return (
            <Provider store={store}>
                <Container />
            </Provider>
        );
    };
    
    export default App;
    

예제 코드 - with reduxjs

  • todoSlice.js

    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
        isFetching: false,
        todoList: [],
        todo: {},
        error: {},
    };
    
    const todoSlice = createSlice({
        name: 'todo',
        initialState,
        reducers: {
            fetchTodoList: (state, action) => {
                state.isFetching = true;
                state.todoList = [];
                state.todo = {};
                state.error = {};
            },
    
            fetchTodoListSuccess: (state, action) => {
                state.isFetching = false;
                state.todoList = action.payload;
            },
    
            fetchTodoListFail: (state, action) => {
                state.isFetching = false;
                state.error = action.payload;
            },
    
            fetchTodo: (state, action) => {
                state.isFetching = true;
                state.todo = {};
                state.error = {};
            },
    
            fetchTodoSuccess: (state, action) => {
                state.isFetching = false;
                state.todo = action.payload;
            },
    
            fetchTodoFail: (state, action) => {
                state.isFetching = false;
                state.error = action.payload;
            },
        },
    });
    
    export default todoSlice;
    
  • todoSaga.js

    import { call, put, select, takeLatest } from 'redux-saga/effects';
    import axios from 'axios';
    import todoSlice from './todoSlice';
    
    const handleFetchTodoList = function* () {
        try {
            const response = yield call(() => axios.get(`https://jsonplaceholder.typicode.com/todos`));
    
            // put : dispatch와 동일한 효과
            yield put(todoSlice.actions.fetchTodoListSuccess(response.data));
        } catch (error) {
            yield put(todoSlice.actions.fetchTodoListFail(error));
        }
    };
    
    const handleFetchTodoListSuccess = function* (action) {
        const data = action.payload;
        const id = data[0].id;
    
        try {
            const response = yield call(
                (id) => axios.get(`https://jsonplaceholder.typicode.com/todos/${id}`),
                id,
            );
    
            yield put(todoSlice.actions.fetchTodoSuccess(response.data));
        } catch (error) {
            yield put(todoSlice.actions.fetchTodoFail(error));
        }
    };
    
    const handleFetchTodoSuccess = function* () {
        // select : useSelector와 동일한 효과
        const state = yield select(({ todoReducer }) => todoReducer);
    
        console.log('handleFetchTodoSuccess', state);
    };
    
    const todoSaga = [
        // todoSlice의 fetchTodoList action을 dispatch하게되면 handleFetchTodoList generator function 호출
        takeLatest(todoSlice.actions.fetchTodoList, handleFetchTodoList),
    
        // todoSlice의 fetchTodoListSuccess action을 dispatch하게되면 handleFetchTodoListSuccess generator function 호출
        takeLatest(todoSlice.actions.fetchTodoListSuccess, handleFetchTodoListSuccess),
    
        // todoSlice의 fetchTodoSuccess action을 dispatch하게되면 handleFetchTodoSuccess generator function 호출
        takeLatest(todoSlice.actions.fetchTodoSuccess, handleFetchTodoSuccess),
    ];
    
    export default todoSaga;
    
  • todoStore.js

    import createSagaMiddleware from 'redux-saga';
    import { applyMiddleware, combineReducers, createStore } from 'redux';
    import { all } from 'redux-saga/effects';
    import todoSlice from './todoSlice';
    import todoSaga from './todoSaga';
    
    const rootSaga = function* () {
        yield all([...todoSaga]);
    };
    
    const createTodoStore = () => {
        const sagaMiddleware = createSagaMiddleware();
    
        const store = createStore(
            combineReducers({
                todoReducer: todoSlice.reducer,
            }),
            applyMiddleware(sagaMiddleware),
        );
    
        sagaMiddleware.run(rootSaga);
    
        return store;
    };
    
    export default createTodoStore;
    
  • index.js

    import React from 'react';
    import { Provider, useDispatch, useSelector } from 'react-redux';
    import createTodoStore from './todoStore';
    import todoSlice from './todoSlice';
    
    const store = createTodoStore();
    
    const Container = () => {
        const todo = useSelector(({ todoReducer }) => todoReducer.todo);
        const dispatch = useDispatch();
    
        return (
            <div>
                <button onClick={() => dispatch(todoSlice.actions.fetchTodoList())}>try fetch</button>
                <div>{todo.title}</div>
            </div>
        );
    };
    
    const App = () => {
        return (
            <Provider store={store}>
                <Container />
            </Provider>
        );
    };
    
    export default App;
    

참고

반응형

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

[React] WebSocket  (1) 2020.12.30
[React] react-redux  (0) 2020.12.30
[React] Library  (0) 2020.12.30
[React] react-testing-library  (0) 2019.12.29
[React] Enzyme  (0) 2019.12.28

+ Recent posts