글쓰는 개발자

[React/react-native] Redux 쉽게 사용하기 (3/3) 본문

Development/React

[React/react-native] Redux 쉽게 사용하기 (3/3)

세가사 2024. 5. 30. 09:45
반응형

https://gran007.tistory.com/entry/Reactreact-native-Redux-%EC%89%BD%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-23

 

[React/react-native] Redux 쉽게 사용하기 (2/3)

https://gran007.tistory.com/entry/Reactreact-native-Redux-%EC%89%BD%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-12 [React/react-native] Redux 쉽게 사용하기 (1/2)React의 상태관리는 useState를 사용해 이루어 진다.import { useState } fr

gran007.tistory.com

 

리듀서 자동화

 

코드의 템플릿화가 완료되면 이를 자동화 시킬 방법이 있는지 생각해봐야한다.

count.js를 다시 보자.

const COUNT_REDUCER_TYPE = "COUNT_REDUCER_TYPE";

export const setCount = (payload) => { type: COUNT_REDUCER_TYPE, payload }

const initalState = {
  count: 0,
  username: 'not defined',
  timer: '00:00:00'
};

const counter = (state = initalState, action) => {
  switch (action.type) {
    case INCRESE:
      return {
        ...state,
        count: ...action.payload
      };
    default:
      return state;
  }
};

export default counter;

 

이제 리듀서는 위와 같은 형태를 유지하고 innitialState만 달라진다. 타입명과 setCount등 리듀서를 호출하는 함수의 이름을 임의로 지정한다면 리듀서는 자동화가 가능하다.

 

 

하지만 count나 userInfo등, 리듀서 이름과 해당 리듀서에 intialState는 사용자가 지정해야 하기 때문에 다음과 같이 리듀서를 구성해보자.

 

export const initialStates = {
  counter: {
    count: 0,
    timer: '00:00:00'
  },
  userInfo: {
    name: 'none',
    age: 0
  }
}

 

위 initialState를 이용해서 combineReducer에 들어갈 리듀서함수를 자동생성한다.

const reducers = Object.keys(initialState).reduce((reducers, type) => {
        reducers[type] = (state = initialState[type], action) => {
            switch (action.type) {
                case type: {
                    return {
                        ...state,
                        ...action.payload,
                    }
                }
                default:
                    return state;
            }
        }
        return reducers;
    }, {})

 

 

위 함수가 실행되면 reducers 변수는 다음과 같이 리듀서별 switch문이 들어간 함수가 생성된다.

// reducers

{
  counter: (state={ count: 0, timer: '00:00:00' }, action) => {
  	switch(action.type) {
    ...
  },
  userInfo: (state={ name: 'none', age: 0 }, action) => {
  	switch(action.type) {
    ...
  },
}

 

이제 combineReducer함수를 이용해 위 함수 오브젝트를 실제 reducer로 묶은 뒤 Provider에 들어갈 store를 만들어준다.

reduxFactory.js

import { combineReducers } from 'redux';
import { configureStore } from '@reduxjs/toolkit'

...

const rootReducer = combineReducers(reducers);
export const store = configureStore({reducer: rootReducer});

 

 

이제 해당 store를 index.js에 provider에 넣어주면 reducer의 연동이 완료된다. 

import React from 'react';
import { store } from './reduxFactory'
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
);

export default App;

 

 

reducer는 만들었지만 기존에 setCount와 같은 타입과 payload로 dispatch하는 action도 각각 만들어 주어야 한다. 이 또한 다음 방식으로 자동화가 가능하다.

reducerFactory.js

....

const getSetterName = (name) => `set${name[0].toUpperCase()}${name.slice(1)}`;

export const actions = Object.keys(initialStates).reduce((modules, type) => {
    modules[getSetterName(type)] = (payload) => {
        store.dispatch({
            type,
            payload,
          });
    }
    return modules;
  }, {});

 

위 생성문으로 actions라는 변수는 함수형 오브젝트로 재구성된다.

// actions

{
  setCounter: (payload) => {
    store.dispatch({ type: 'counter', payload })
  },
  setUserInfo: (payload) => {
    store.dispatch({ type: 'userInfo', payload })
  }
}

 

중간에 store.dispatch를 빼고 단순히 type과 payload를 리턴하는 함수로 만들어도 무관하다. 만약 그렇게 구성을 한다면 리액트에서 사용할때 매번 useDispatch함수를 호출해서 dispatch를 만든뒤 actions에 함수를 호출해야 한다. 그 작업이 번거롭다면 위와 같이 store.dispatch를 호출해서 actions 안에 넣어버려도 괜찮다.

 

 

이제 actions를 만들었으니 reducer에 상태값을 변경해 보자.

App.js

import { useSelector } from 'react-redux';
import { actions } from './reduxFactory';

const App = () => {

  const { count } = useSelector(state => state.counter);

  return (
    <div>
      {count}
      <button onClick={() => {
        actions.setCount({ count: count + 1 })
      }}>Add</button>
    </div>
  );
};

export default App;

 

자동화 코드라 복잡해 보이지만 reduxFactory.js를 한번만 만들어 두면 reducer에 initialState만 변경하는것으로 reducer와 actions 까지 자동 생성된다. 이제 initialState 오브젝트 하나만 관리하면 손쉽게 reducer를 추가해서 redux를 사용할 수 있다.

 

마지막으로 정리된 reduxFactory코드를 공유한다.

redux 자동화 코드

reducerState.js

export const initialStates = {
  counter: {
    count: 0,
    timer: '00:00:00'
  },
  userInfo: {
    name: 'none',
    age: 0
  }
}

 

reduxFactory.js

import { initialState } from './reducerState'
import { configureStore } from '@reduxjs/toolkit'
import { combineReducers } from 'redux';

const getSetterName = (name) => `set${name[0].toUpperCase()}${name.slice(1)}`;

// generate reducer
const reducer = combineReducers(
    Object.keys(initialState).reduce((reducers, type) => {
        reducers[type] = (state = initialState[type], action) => {
            switch (action.type) {
                case type: {
                    return {
                        ...state,
                        ...action.payload,
                    }
                }
                default:
                    return state;
            }
        }
        return reducers;
    }, {})
);

export const store = configureStore({reducer});

// generate actions
export const actions = Object.keys(initialState).reduce((modules, type) => {
    modules[getSetterName(type)] = (payload) => {
        store.dispatch({
            type,
            payload,
          });
    }
    return modules;
  }, {});

 

위 코드와 개념을 참고하면 복잡한 redux를 간편하게 이용할수 있다.

반응형