redux toolkit todolist tutorial

HyosikPark·2021년 2월 20일
3

react + redux

목록 보기
1/2

why?

apollo, graphql 조합이 rest API data를 관리하기에는 redux보다 적절하다고 생각하지만, 그렇다고 frontend 전반의 전역 상태관리를 대체할 수는 없는 것이다.

이 외에도 테스트와 디버깅이 쉽고, 변경된 상태에 관해서만 rendering이 발생하여 최적화가 잘 되어 있다.

middleware 사용으로 sideEffect나 비동기 관련 로직 작성시에도 유용하다.

redux-toolkit

redux를 좀 더 효율적으로 사용할 방법이 없는지 검색중에 velopert님의 글을 참고하게 되었고 redux-toolkit으로 action, reducer를 간단하게 작성할 수 있는 방법을 알게 되었다.

tutorial

// modules/todos.ts

import { createSlice, PayloadAction } from '@reduxjs/toolkit';

export type Todo = {
  id: number;
  text: string;
  done: boolean;
};

type TodosState = Todo[];

const initialState: TodosState = [
  { id: 1, text: '타입스크립트 배우기', done: true },
  { id: 2, text: '타입스크립트와 리덕스 함께 사용해보기', done: true },
  { id: 3, text: '투두리스트 만들기', done: false },
];

export const todosSlice = createSlice({
  name: 'todos',
  initialState,
  reducers: {
    addTodo(state, action: PayloadAction<string>) {
      const nextId = Math.max(...state.map((todo) => todo.id)) + 1;
      state.push({ id: nextId, text: action.payload, done: false });
    },
    toggleTodo(state, action: PayloadAction<number>) {
      return state.map((todo) => {
        return todo.id === action.payload
          ? { ...todo, done: !todo.done }
          : todo;
      });
    },
    removeTodo(state, action: PayloadAction<number>) {
      return state.filter((todo) => todo.id !== action.payload);
    },
  },
});

export const { addTodo, toggleTodo, removeTodo } = todosSlice.actions;

export default todosSlice.reducer;

기존의 action, reducer, initState를 createSlice 인자 객체에 통합할 수 있게 되었다.

그리고 기존 recucer의 경우에 state 인자를 immutable하게 사용해야 했기에 action.type case에 따라 return하는 방법이 까다로웠다.

toolkit의 장점은 state 인자를 수정해도 문제가 되지 않는 다는 것이다.

// modules/index.ts

import { combineReducers, configureStore } from '@reduxjs/toolkit';
import todosReducer from './todos';

const rootReducer = combineReducers({
  todos: todosReducer,
});

const store = configureStore({
  reducer: rootReducer,
  // middleware, devtool 등 추가 가능
});

export type RootState = ReturnType<typeof store.getState>;
export default store;



// index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './modules/index';

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

store에 reducer를 비롯한 middleware, devtool 등의 도구들을 통합할 수 있게 되었다.

// hook/useTypedSelector.ts

import { TypedUseSelectorHook, useSelector } from 'react-redux';
import { RootState } from '../modules';

export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;

// components/todoList.tsx

const TodoList = () => {
//  const todos: Todo[] = useSelector((state: RootState) => state.todos);
    const todos: Todo[] = useTypedSelector((state) => state.todos);

  
  return (<div></div>)
}

hook으로 useSelector에 toolkit이 제공하는 type을 지정해두고 사용하면 state인자에 type을 매번 지정 해야하는 번거로움을 없앨 수 있다.

// components/todoItem.tsx

import { removeTodo, toggleTodo } from '../modules/todos';
import { useDispatch } from 'react-redux';


const TodoItem = () => {
  const dispatch = useDispatch();
  
  // ....
  
  return (
    <span className='text' onClick={() => dispatch(toggleTodo('text'))}>
  )
}

dispatch는 todosSlice.action에서 반환된 method를 활용한다.

다음은 redux-saga를 공부해 봐야겠다.

0개의 댓글