RN + TS 프로젝트에 Redux Toolkit 적용

Junghyun Park·2021년 8월 30일
2

Why Redux Toolkit?

https://kyounghwan01.github.io/blog/React/redux/redux-toolkit/

redux를 아무 라이브러리 없이 사용할 때 (actionType 정의 -> 액션 함수 정의 -> 리듀서 정의) 1개의 액션을 생성하는 방식은 너무 많은 코드가 생성되니

redux-actons라는 것을 사용하게 되었고,
불변성을 지켜야하는 원칙 때문에 immer를 사용하게되고,
store 값을 효율적으로 핸들링하여 불필요 리렌더링을 막기 위해 reselect를 쓰게 되었으며,
비동기를 수월하게 하기위해, thunk나 saga를 설치하여 redux를 더 효율적으로 사용하는 등 여러 라이브러리슬 설치해야 원활한 사용이 가능

그런데, redux-toolkit은 redux가 공식적으로 만든 라이브러리로, saga를 제외한 위 기능 모두 지원하고, typeScript 사용자를 위해 action type, state type 등 TypeScript를 사용할 때 필요한 Type Definition을 공식 지원

프로젝트 적용 실습

1. 설치

# prod packages
npm install redux react-redux @reduxjs/toolkit
# dev package
npm install -D @types/react-redux

2. store 파일 생성 (in src/redux/index.ts)

import {configureStore} from '@reduxjs/toolkit';
import {loginSlice} from './slices/login';

export const store = configureStore({
  reducer: {
    loginReducer: loginSlice.reducer,
  },
});

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type store
export type AppDispatch = typeof store.dispatch;

3. hooks(dispatch, useSlector) 파일 생성

//일반적인 useDipatch와 useSelector의 typed version을 export
import {TypedUseSelectorHook, useDispatch, useSelector} from 'react-redux';
import type {RootState, AppDispatch} from './store';

//useSelector를 직접 사용하는 경우, 매번 (state:RootState)를 붙여줘야 하므로 변환
//useDispatch를 직접 사용하는 경우, thunks를 인식하지 못하는 문제 해결
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

4. slice 파일 생성 (login.ts)

  • initial state, slice name, reducer들로 구성된 object를 인자로 받아서, 자동으로 action creators와 action types를 생성 (내부적으로는 createActioncreateReducer를 실행)
  • redux 기본 사용법과 달리, state를 {...} 식으로 업데이트 하지 않고, =를 통해 곧바로 새로운 값 할당 가능
import {createSlice, PayloadAction} from '@reduxjs/toolkit';

// Define a type for the slice state
export interface UserInfoState {
  userInfo: {
    token: string;
    userType: 'email' | 'kakao' | '';
    userName?: string;
    email?: string;
    profilePicUrl?: string;
    age?: number;
  };
}

// Define the initial state using that type
const initialState: UserInfoState = {
  userInfo: {
    token: '',
    userType: '',
    userName: '',
    email: '',
    profilePicUrl: '',
    age: 0,
  },
};

export const loginSlice = createSlice({
  name: 'login',
  // `createSlice` will infer the state type from the `initialState` argument
  initialState,
  reducers: {
    // Use the PayloadAction type to declare the contents of `action.payload`
    storeUserInfo: (state, action: PayloadAction<UserInfoState>) => {
      state.userInfo = action.payload.userInfo;
    },
  },
});

export const {storeUserInfo} = loginSlice.actions;

export default loginSlice.reducer;

5. state 가져오기 및 갱신하기

import React from 'react';
import {SafeAreaView, Image, View, Text} from 'react-native';
import {ThemeProvider} from 'styled-components/native';
import {Theme} from './styles/theme';
import {Provider} from 'react-redux';
import {store} from 'redux/store';

import {TemporaryMySelfMarker} from '@images';

import {useAppDispatch, useAppSelector} from 'redux/hook';
import {storeUserInfo, UserInfoState} from 'redux/slices/login';


const App = () => {
  const Test = (): JSX.Element => {
    const userInfo = useAppSelector(state => state.loginReducer);
    const dispatch = useAppDispatch();
    const userInfoSample: UserInfoState = {
      userInfo: {
       token: 'asdfsdf',
  	   userType: 'email',
  	   userName: 'sdf',
	   email: 'sadf',
	   profilePicUrl: 'asdfsdf',
	   age: 23,
  		},
	 };

    const dispatchUserInfo = () => {
		 dispatch(storeUserInfo(userInfoSample));
    };


    return (
      <View>
        <Image source={TemporaryMySelfMarker} />
        <Text style={{fontSize: 40}} onPress={dispatchUserInfo}>
          Test
        </Text>
      </View>
    );
  };

  return (
    <Provider store={store}>
      <ThemeProvider theme={Theme}>
        <SafeAreaView>
          <Test />
        </SafeAreaView>
      </ThemeProvider>
    </Provider>
  );
};

export default App;
profile
21c Carpenter

1개의 댓글

comment-user-thumbnail
2022년 12월 22일
  1. store 파일 생성 (in src/redux/index.ts) 에서 store.tx로 파일명이 변경되어야 할것 같습니다! 도움되는글 감사합니다.
답글 달기