[완결] redux-toolkit-todo

undefcat·2021년 4월 29일
2

redux-toolkit-todo

목록 보기
4/4
post-thumbnail

redux-toolkit-todo

남은 기능들을 하나씩 구현하고 시리즈를 마치도록 하겠습니다.

아이템 삭제하기

// src/components/Main/Todo.js

import { useDispatch } from 'react-redux'
// 👇👇👇
import {
  check as checkTodo,
  remove as removeTodo,
} from '../../state/todos'

function Todo({ id, done, text }) {
  const dispatch = useDispatch()

  return (
    <li className={ done ? 'completed' : '' }>
      <div className="view">
        <input
          checked={ done }
          className="toggle"
          onChange={ e => dispatch(checkTodo({ id, checked: e.target.checked })) }
          type="checkbox"
        />
        <label>{ text }</label>
        <button
          className="destroy"
          // 👇👇👇
          onClick={ () => dispatch(removeTodo(id)) }
        />
      </div>
      <input className="edit" value="Create a TodoMVC template" />
    </li>
  )
}

export default Todo

아이템 수정하기

// src/components/Main/Todo.js

import { useState, useEffect, useRef } from 'react'
import { useDispatch } from 'react-redux'
import {
  check as checkTodo,
  remove as removeTodo,
  edit as editTodo,
} from '../../state/todos'

function getClassName({ done, isEdit }) {
  const classNames = []
  if (done) {
    classNames.push('completed')
  }

  if (isEdit) {
    classNames.push('editing')
  }

  return classNames.join(' ')
}

function Todo({ id, done, text }) {
  const dispatch = useDispatch()
  const [value, setValue] = useState(text)
  const [isEdit, setEdit] = useState(false)
  const inputEl = useRef(null)

  useEffect(() => {
    if (isEdit) {
      inputEl.current.focus()
    }
  }, [isEdit])

  const edit = () => {
    const text = value.trim()
    if (text === '') {
      return
    }

    setEdit(false)
    dispatch(editTodo({ id, text }))
  }

  const handleEnter = e => {
    if (!(e.keyCode === 13 || e.key === 'Enter')) {
      return
    }

    edit()
  }

  const className = getClassName({ done, isEdit })
  return (
    <li className={ className }>
      <div className="view">
        <input
          checked={ done }
          className="toggle"
          onChange={ e => dispatch(checkTodo({ id, checked: e.target.checked })) }
          type="checkbox"
        />
        <label onDoubleClick={ () => setEdit(true) }>{ text }</label>
        <button
          className="destroy"
          onClick={ () => dispatch(removeTodo(id)) }
        />
      </div>
      <input
        className="edit"
        onInput={ e => setValue(e.target.value) }
        onKeyDown={ handleEnter }
        onBlur={ () => setEdit(false) }
        ref={ inputEl }
        value={ value }
      />
    </li>
  )
}

export default Todo

모든 아이템 체크/해제

// src/components/Main/index.js

import { useDispatch, useSelector } from 'react-redux'
import { createSelector } from '@reduxjs/toolkit'
import { checkAll } from '../../state/todos'
import Todo from './Todo'

const todosSelector = state => state.todos.items
const filterTypeSelector = state => state.todos.filterType
const filteredTodosSelector = createSelector(
  todosSelector,
  filterTypeSelector,
  (items, filterType) => {
    switch (filterType) {
      case 'do':
        return items.filter(todo => !todo.done)

      case 'done':
        return items.filter(todo => todo.done)

      default:
        return items
    }
  }
)

const isAllCheckedSelector = state => state.todos.items.every(todo => todo.done)

function Main() {
  const dispatch = useDispatch()
  const todos = useSelector(filteredTodosSelector)
  const Todos = todos.map(todo => <Todo key={ todo.id } { ...todo }/>)

  const isAllChecked = useSelector(isAllCheckedSelector)

  return (
    <section className="main">
      <input
        checked={ isAllChecked }
        className="toggle-all"
        id="toggle-all"
        onChange={ e => dispatch(checkAll(e.target.checked)) }
        type="checkbox"
      />
      <label htmlFor="toggle-all">Mark all as complete</label>
      <ul className="todo-list">
        { Todos }
      </ul>
    </section>
  )
}

export default Main

마치며

이렇게 redux-toolkit에 대해 알아보았습니다. 개인적으로 redux가 조금 친숙한 상태에서 redux-toolkit을 사용하니 너무 편리한 것 같습니다.

redux-toolkit은 redux의 이해가 바탕이 되어야만 사용할 수 있기 때문에, redux를 먼저 공부하시고 사용하시기를 추천드립니다. 또한 리액트 생태계에서 hook은 더 이상 신기술이 아닌 기본기 수준까지 온 것 같다는 생각이 듭니다. recoil이나 redux에서도 이미 use~ hook API를 사용하는 점을 보니 더 그런 것 같습니다.

날로 발전하는 리액트를 보니, 개인적으로 현재 Vue는 어디까지 왔는지 궁금합니다. Vue 3.0이 작년 말에 정식 릴리즈 되었는데, 사실 가장 큰 변경점은 react hook과 같은 API가 생겼다는 점입니다. Vue에선 이를 Composition API라고 부르고 있습니다.

앞으로도 계속 열심히 공부해야겠다는 생각이 듭니다. 정말 순식간에 바뀌는 것 같습니다. 뒤처지지 않기 위해 많은 노력을 해야겠습니다.

다음 시리즈는 약간 실전용으로 redux를 활용하는 예제를 구상하고 있습니다. 지금까지 recoil, redux를 정리했던 이유가 회사일을 위한 공부 용도였던만큼, 실무에 가까운 예제를 정리해보도록 하겠습니다.

그럼 다음에 만나요~~~🖐🖐🖐🖐

(전체 코드는 여기에서 확인할 수 있습니다.)

profile
undefined cat

0개의 댓글