TypeScript React + Recoil을 이용한 TodoList 만들어보기 📘

yiyb0603·2021년 2월 5일
41

React

목록 보기
8/15
post-thumbnail

안녕하세요! 오늘은 최근에 출시된 React의 상태관리 라이브러리, Recoil을 이용한 TodoList를 만들어 볼것입니다. 기존에 저는 다수의 Mobx 경험과 소수의 Redux를 이용하여 상태관리를 해왔었는데요, 이번에 페이스북에서 공식으로 발표한 새로운 패러다임의 상태관리 라이브러리 이기에 한번 사용해보고 싶었습니다.

1. Recoil의 등장배경

기존에 많은 사람들이 사용중인 상태관리 라이브러리 Redux, MobX는 그저 단순 자바스크립트 상태관리 라이브러리 일뿐, 리액트를 위한 상태관리 라이브러리는 아니었습니다. 또한 제가 느낀바로는 MobX와 Redux는 사람들간의 코드 짜는 스타일이 많이 다른 라이브러리였고, 공식문서 에서도 올바른 방향을 가이드 하고있지는 않았습니다.

하지만 2020년 5월, React europe 2020에서 페이스북이 발표한 리액트 상태관리 라이브러리 Recoil은 최대한 Reactish 하게 문법이 이루어져있으며 오직 리액트를 위한 상태관리 라이브러리 입니다.

Redux나 MobX를 사용하기 위해서는 store 구성을 위한 package.json에 있는 CRA 설정부터 action, reducer 등등의 기본적인 보일러 플레이트를 설계해야하고, 그에 따른 코드들을 적어주어야 비로소 환경설정이 끝나게 됩니다. 하지만 이번에 Recoil을 써본결과, 위와같은 과정은 전혀 없었으며 그저 RecoilRoot를 최상위로 감싸준다면 사실상 프로젝트 설정은 모두 끝난셈입니다.

2. 프로젝트 생성 및 설정

TypeScript + React + Recoil을 이용한 TodoList 프로젝트를 생성하겠습니다.
cmd를 이용하여 원하는 경로로 들어간 뒤, 아래의 문장을 입력해주세요.

create-react-app recoil_todolist --template typescript

프로젝트 생성이 완료가 되면, 프로젝트를 열어서 Recoil 설정을 해줍시다.
먼저 yarn을 이용하여 필요한 node-modules들을 설치해주도록 하겠습니다.

yarn add node-sass recoil react-icons

해당 패키지들은 순서대로 scss, 리코일, 리액트 아이콘 패키지를 설치해주었습니다.
설치가 완료되었다면 root 경로의 index.tsx를 열어서 아래와 같이 수정해주세요!

import React, { StrictMode } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { RecoilRoot } from 'recoil';
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
import App from 'components/App';

// 이외에 필요한 스타일 파일이 있다면 import 해주세요.

ReactDOM.render(
  <StrictMode>
      <RecoilRoot> {/* RecoilRoot provider를 이용하여 recoil을 사용가능하도록 설정해줍니다. */}
        <Router>
          <App />
        </Router>
      </RecoilRoot>
    </StrictMode>,
  document.getElementById('root')
);

reportWebVitals();

위와 같이 설정을 마친 다음, yarn start를 이용하여 프로젝트 실행시 오류가 뜨지 않는다면 세팅을 모두 마쳤습니다. Redux나 MobX보다 훨씬 더 설정이 간단한 것 같아요.

3. 컴포넌트 생성하기

이제 저희가 Todo List를 만들기 위해서 컴포넌트 분할 작업을 해야합니다. 저같은 경우는 아래의 사진처럼 컴포넌트를 분할 하여 생성했습니다.

  • TodoInput: 추가할 Todo List를 입력하는 입력창
  • TodoList: 자신이 추가한 TodoList 아이템 리스트
  • TodoModal: 자신이 수정할 Todo를 선택하여 수정하는 모달창
  • TodoTemplate: 전체 컴포넌트들을 보여주고, 감싸고 있는 템플릿 컴포넌트
  • TodoTitle: 만들 TodoList의 제목 타이틀

가장 먼저 css 컬러변수들을 정의 해놓은 scss 파일을 만들어 주겠습니다. 해당 파일은 index.tsx에 import 해놓으면, 어느 파일에서나 해당 css 변수들에 접근 가능합니다.

// color.scss
:root {
  --red: #eb2f06;
  --orange: #e67e22;
  --yellow: #f1c40f;
  --green: #2ecc71;
  --blue: #0984e3;
  --skyBlue: #75a4f5;
  --navy: #000080;
  --purple: #6c5ce7;
  --mint: #00d2d3;
  --pink: #e84393;
  --lighterGray: #dfe6e9;
  --gray: #b2bec3;
  --fontGray: #6b6b6b;
  --white: #ffffff;
  --black: #000000;
  --snow: #dff9fb;
  --main: #5982c8;
  --lighterGreen: #55efc4;
  --background: #ffffff;
}

그 다음, 부모 컴포넌트에 위치한 TodoTemplate.tsxscss파일을 아래와 같이 작성하여 디자인을 했습니다.

// TodoTemplate.tsx
import React from 'react';
import './TodoTemplate.scss';
import TodoInput from 'components/TodoInput';
import TodoList from 'components/TodoList';
import TodoTitle from 'components/TodoTitle';

const TodoTemplate = (): JSX.Element => {
  return (
    <div className='TodoTemplate'>
      <div className='TodoTemplate-Contents'>
        <TodoTitle />
        <TodoList />
        <TodoInput />
      </div>
    </div>
  );
};

export default TodoTemplate;

아래는 TodoTemplate.scss 파일입니다.

// TodoTemplate.scss
.TodoTemplate {
  width: 100%;
  min-height: 100%;
  background-color: var(--main);
  display: flex;
  display: -webkit-flex;
  justify-content: center;

  &-Contents {
    margin: auto;
    display: flex;
    display: -webkit-flex;
    flex-direction: column;
    -ms-flex-direction: column;
    align-items: center;
  }
}

이제 저희가 만들 TodoList의 제목 컴포넌트인 TodoTitle을 스타일링 해주겠습니다. 저는 아래와 같이 코드를 짜면서 디자인을 해주었어요!

// TodoTitle.tsx
import React from 'react';
import { GiWireCoil } from 'react-icons/gi';
import './TodoTitle.scss';

const TodoTitle = (): JSX.Element => {
  return (
    <div className='TodoTitle'>
      <GiWireCoil className='TodoTitle-Icon' />
      <div className='TodoTitle-Title'>TodoList With Recoil</div>
    </div>
  );
};

export default TodoTitle;

아래는 TodoTitle.scss 파일입니다.

// TodoTitle.scss
.TodoTitle {
  display: flex;
  display: -webkit-flex;
  align-items: center;
  font-size: 1.8rem;
  color: var(--white);
  margin-bottom: 10px;

  &-Icon {
      margin-right: 8px;
  }
}

이제, Todo들이 목록으로 띄워질 TodoList 컴포넌트를 작성해줄게요! 저는 아래와 같이 코드를 작성하여 디자인을 해주었습니다.

// TodoList.tsx
import React, { useCallback } from 'react';
import './TodoList.scss';

const TodoList = (): JSX.Element => {
  return (
    <div className='TodoList'>
      <div className='TodoList-NoList'>Todo가 없습니다. 자유롭게 추가해보세요!</div>
    </div>
  );
};

export default TodoList;

아래는 TodoList.scss 코드입니다.

// TodoList.scss

.TodoList {
  width: 500px;
  height: 500px;
  max-height: 500px;
  position: relative;
  border: 2px solid var(--white);
  border-radius: 10px;
  margin-bottom: 10px;
  padding: 1.5rem;
  overflow-x: hidden;
  overflow-y: auto;

  &-NoList {
    width: 100%;
    text-align: center;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}

일단 현재까지 코드를 작성하셨다면, 해당 컴포넌트들을 TodoTemplate.tsx 파일에서 불러주어 모습을 확인해보세요!

import React from 'react';
import TodoList from 'components/TodoList';
import TodoTitle from 'components/TodoTitle';
import './TodoTemplate.scss';

const TodoTemplate = (): JSX.Element => {
  return (
    <div className='TodoTemplate'>
      <div className='TodoTemplate-Contents'>
        <TodoTitle />
        <TodoList />
      </div>
    </div>
  );
};

export default TodoTemplate;

이제 Todo를 추가할 TodoInput 컴포넌트를 추가해볼게요! 아래와 같이 코드를 짜면서 저는 디자인을 해주었습니다.

import React from 'react';
import { FaPen } from 'react-icons/fa';
import './TodoInput.scss';

const TodoInput = (): JSX.Element => {
  return (
    <div className='TodoInput'>
      <input
        type='text'
        className='TodoInput-Input'
        placeholder='Todo를 입력해보세요!'
      />
      <FaPen className='TodoInput-Button' onClick={addTodo} />
    </div>
  );
};

export default TodoInput;

아래는 TodoInput.scss 파일입니다.

.TodoInput {
  width: 100%;
  display: flex;
  display: -webkit-flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 2px solid var(--white);
  padding: 0 4px;

  &-Input {
    flex: 1;
    outline: none;
    border: none;
    color: var(--white);
    background-color: var(--main);
    padding: 5px 2px;
    font-size: 1.2rem;

    &::placeholder {
        color: var(--white);
    }
  }

  &-Button {
    color: var(--white);
    font-size: 1.4rem;
    cursor: pointer;
  }
}

짜잔! 이제 전반적인 컴포넌트 템플릿 제작은 모두 끝이났습니다. 남은 컴포넌트들은 직접 기능을 구현하면서 만들어 볼 것입니다.

4. Recoil 파일 작성하기

이제 본문의 중심이라고 할 수 있는 Recoil을 이용하여 Todo를 구현해보도록 하겠습니다. root 디렉토리 경로에 recoil 이라는 폴더를 만들고, todo.ts라는 파일을 만들어주세요! 우리가 이제 코드를 작성하기 전에, Recoil의 개념 중 하나인 atom에 대하여 잠깐 알아보겠습니다. (IDE 아닙니다 ㅎㅎ)

atom: 상태의 단위

  • 전역 상태관리가 되는 대상이며, 해당 atom의 값이 변경되면 이 값을 구독하고있던 컴포넌트들이 모두 리렌더링이 됩니다.

  • 고유한 key값을 가져야 하며 기본값 default를 설정 해줄 수 있습니다.

  • 개인적으로 더 자유로운 MobX의 observable의 느낌이 들기도 합니다.

selector: atom 혹은 selector를 받아 쓰는 순수함수

(출처: 휴먼스케이프 기술 블로그: https://medium.com/humanscape-tech/recoil-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-285b29135d8e)

  • 인자로 받은 atom들이나 selector들 중 어떤 것이 업데이트 된다면 selector 함수는 다시 평가됩니다.

  • atom처럼 컴포넌트에서 구독할 수 있으며, selector 함수가 다시 평가될 때, 컴포넌트가 리렌더링이 됩니다.

위의 두가지가 대표적인 Recoil에서 사용하는 개념인데요, 제가 TodoList를 만들면서 selector는 사용할 일이 딱히 없다고 느껴서 atom의 상태를 관리하는 방식의 Recoil이 쓰일 것입니다.

개념을 모두 알아봤으니 이제 코드를 작성해봅시다! src/recoil/todo.ts 파일에 아래의 코드를 작성해주세요!

// src/recoil/todo.ts
import { atom } from "recoil";

export interface ITodoTypes {
  id: number;
  contents: string;
  isCompleted: boolean;
}

// TodoInput에서 입력하는 값을 atom으로 관리하는 방식
export const inputState = atom<string>({
  key: 'inputState',
  // key의 값은 항상 고유값이어야 합니다.
  
  default: '',
});

// 업데이트 시킬 Todos atom 배열
export const todosState = atom<ITodoTypes[]>({
  key: 'todos',
  
  // default에는 임의의 데이터를 넣어줍시다.
  default: [
    {
      id: 1,
      contents: 'Todo List를',
      isCompleted: false,
    },

    {
      id: 2,
      contents: '자유롭게',
      isCompleted: false,
    },

    {
      id: 3,
      contents: '추가해보세요!',
      isCompleted: false,
    }
  ],
});

임의의 데이터를 넣어준 todosState atom 배열을 컴포넌트에다가 렌더링을 해주도록 하려고 하는데요, 이제 기능들을 위한 컴포넌트들을 추가로 생성해주도록 하겠습니다. 😊

src/components/TodoList 디렉토리 안에 TodoItem 이라는 컴포넌트를 생성해주세요! 그 다음, 아래의 코드를 작성해줍시다.

import React, { useCallback, useState } from 'react';
import { ITodoTypes } from 'recoil/todo';
import { FaPen } from 'react-icons/fa';
import { MdClose } from 'react-icons/md';
import { SetterOrUpdater } from 'recoil';
import './TodoItem.scss';

interface PropTypes {
  id: number;
  contents: string;
  isCompleted: boolean;
  
  onComplete: (id: number) => void;
  onDelete: (id: number) => void;
  
  todos: ITodoTypes[];
  setTodos: SetterOrUpdater<ITodoTypes[]>;
}

const TodoItem = ({
  id,
  contents,
  isCompleted,
  onComplete,
  onDelete,
  todos,
  setTodos,
}: PropTypes): JSX.Element => {
  const [isModal, setIsModal] = useState<boolean>(false);
  const [modifyContents, setModifyContents] = useState<string>('');

  const onModify = useCallback((): void => {
    setIsModal(true);
    setModifyContents(contents);
    // 선택한 Todo의 내용을 default value로 지정하는 작업
  }, [contents]);

  const onModifyTodo = useCallback((): void => {
    if (!modifyContents.trim()) {
      return;
    }

    // Todo 업데이트 확인을 눌렀을때 객체 업데이트
    setTodos(todos.map((todo: ITodoTypes) => {
      return todo.id === id ? { ...todo, contents: modifyContents } : todo;
    }));

    setIsModal(false);
  }, [id, modifyContents, setTodos, todos]);

  return (
    <>
      <div className='TodoItem'>
        <div
          title={contents}
          className={isCompleted ? 'TodoItem-Completed' : ''}
          onClick={() => onComplete(id)}
        >
          {contents}
        </div>

        <div className='TodoItem-Icons'>
          <FaPen className='TodoItem-Icons-Pen' onClick={onModify} />
          <MdClose className='TodoItem-Icons-Close' onClick={() => onDelete(id)} />
        </div>
      </div>
    </>
  );
};

export default TodoItem;

아래는 TodoItem.scss 코드입니다.

.TodoItem {
  display: flex;
  display: -webkit-flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--white);
  font-size: 1.2rem;

  -moz-user-select: none;
  -webkit-user-drag: none;

  &-Completed {
    text-decoration-line: line-through;
    text-decoration-color: var(--red);
  }

  & > * {
    max-width: 100%;
    max-height: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
  }

  &-Icons {
    display: flex;
    display: -webkit-flex;
    align-items: center;

    &-Pen {
      color: var(--green);
    }

    &-Close {
       color: var(--red);
       font-size: 1.9rem;
    }
  }
}

5. atom 배열을 이용한 상태관리

이제 atom에 선언해두었던 todosState 배열을 불러와서 상태관리를 해주겠습니다. 먼저, 배열을 map하여 렌더링을 해주도록 하겠습니다. 아래의 코드를 TodoList.tsx에 추가하여 수정해주세요! 추가적으로 완료 표시, 삭제를 구현하는 함수들도 추가하도록 하겠습니다.

// TodoList.tsx
import React, { useCallback } from 'react';
import { useRecoilState } from 'recoil';
import { todosState, ITodoTypes } from 'recoil/todo';
import TodoItem from './TodoItem';
import './TodoList.scss';

const TodoList = (): JSX.Element => {
  const [todos, setTodos] = useRecoilState<ITodoTypes[]>(todosState);

const onComplete = useCallback((id: number): void => {
    setTodos(todos.map((todo: ITodoTypes) => {
      // 매개변수로 받은 id와 같은 객체만 완료상태 업데이트
      return todo.id === id ? {...todo, isCompleted: !todo.isCompleted} : todo;
    }));
  }, [setTodos, todos]);

  const onDelete = useCallback((id: number) => {
    // 매개변수로 받은 id와 동일하지 않는 객체들만 필터링
    setTodos(todos.filter((todo: ITodoTypes) => todo.id !== id));
  }, [setTodos, todos]);

  return (
    <div className='TodoList'>
      {
        todos.length > 0 ? todos.map((todo: ITodoTypes) => {
          const { id, contents, isCompleted } = todo;

          return (
            <TodoItem
              key={id}
              id={id}
              contents={contents}
              isCompleted={isCompleted}
              onComplete={onComplete}
              onDelete={onDelete}
              todos={todos}
              setTodos={setTodos}
            />
          );
        }) :

        <div className='TodoList-NoList'>Todo가 없습니다. 자유롭게 추가해보세요!</div>
      }
    </div>
  );
};

export default TodoList;

아래와 같이 스타일 및 렌더링이 되는작업, Todo의 내용을 클릭하였을때 빨간줄이 그이는 완료표시, 그리고 X 버튼을 눌렀을시 리스트에서 Todo가 지워진다면 렌더링 작업, 완료 표시 및 삭제 기능을 완성하였습니다.

TodoList.tsx 코드에서 useRecoilState란 무엇일까요? 언뜻보면 React hooks인 useState와도 유사하게 보입니다. 네. 사용법은 useState와 거의 동일하며, 초기값을 정의할때는 사용할 atom배열을 괄호안에 넣어주면 됩니다.

그리고 구조분해 할당으로 선언되어있는 todos는 초기값으로 선언된 todosState atom 배열을 가리키고 있으며, setTodos는 todos의 상태를 관리해줄 setState입니다. 그리고 setState를 하여 todos의 값을 업데이트 해주면, 실제 todosState의 값도 같이 업데이트되어 리렌더링 됩니다.

위의 기능들을 살펴본다면 useState와 사용법이 거의 유사하지 않나요? 페이스북에서 당시 리액트를 위한 라이브러리라고 소개한 이유중 하나가 바로 리액트에서 쓰이는 문법과 거의 유사하다고 생각이 듭니다. 😊

이제 Todo를 입력할시에, 배열에 값이 추가되도록 하는 작업을 해보겠습니다.
TodoInput.tsx 파일에서 아래의 코드를 추가하여 수정해주세요.

import React, { ChangeEvent, useCallback, KeyboardEvent } from 'react';
import './TodoInput.scss';
import { FaPen } from 'react-icons/fa';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { inputState, todosState, ITodoTypes } from '../../recoil/todo';

const TodoInput = (): JSX.Element => {
  const [contents, setContents] = useRecoilState<string>(inputState);
  
  const todos = useRecoilValue<ITodoTypes[]>(todosState);
  const setTodos = useSetRecoilState<ITodoTypes[]>(todosState);
  
  // useRecoilValue = get 변수
  // useSetRecoilState = setter 지정
  // 위와 같은형식으로 get과 setter를 분리하여 사용하는 방법도 있습니다.

  const addTodo = useCallback((): void => {
    if (!contents.trim()) {
      // 빈칸 입력 방지
      return;
    }

    const nextId: number = todos.length > 0 ? todos[todos.length - 1].id + 1 : 0;
    // 배열에 값이 존재할시, 고유값은 마지막 인덱스에 위치한 id값에서 1을 늘려줘서 고유값 생성.
    // 만약 값이 존재하지 않을시 초기값은 0
    
    const todo: ITodoTypes = {
      id: nextId,
      contents,
      isCompleted: false,
    };

    setTodos([...todos, todo]);
    // 기존 객체들 복사 및 새로운 객체 추가
    
    setContents('');
  }, [contents, setContents, setTodos, todos]);

  const onChange = useCallback((e: ChangeEvent<HTMLInputElement>): void => {
    const { value } = e.target;
    setContents(value);
  }, [setContents]);

  const onKeyDown = useCallback((e: KeyboardEvent<HTMLInputElement>): void => {
    if (e.key === 'Enter') {
      // 엔터키를 눌렀을때.
      addTodo();
    }
  }, [addTodo]);

  return (
    <div className='TodoInput'>
      <input
        type='text'
        className='TodoInput-Input'
        value={contents}
        onChange={onChange}
        placeholder='Todo를 입력해보세요!'
        onKeyDown={onKeyDown}
      />
      <FaPen className='TodoInput-Button' onClick={addTodo} />
    </div>
  );
};

export default TodoInput;

코드를 다 적고나서 컴포넌트를 실행해보세요! Todo를 입력하고 엔터를 눌렀을때 아래의 사진과같이 Todo가 잘 추가가 되나요? 추가가 잘 된다면 성공입니다. 😊

6. Todo 수정하기 작업

이제 마지막으로 수정 기능을 작업하도록 하겠습니다. 수정은 앞서 말했던것 처럼 모달을 이용하여 UI를 꾸밀것이기 때문에 TodoModal 이라는 컴포넌트를 새로 만들어줍시다.

아래와 같이 코드를 작성해주세요! (TodoModal.tsx 파일)

import React, { ChangeEvent, Dispatch, SetStateAction, useCallback } from 'react';
import { FaPen } from 'react-icons/fa';
import './TodoModal.scss';

interface PropTypes {
  setIsModal: Dispatch<SetStateAction<boolean>>;
  modifyContents: string;
  setModifyContents: Dispatch<SetStateAction<string>>;
  onModifyTodo: () => void;
}

const TodoModal = ({
  setIsModal,
  modifyContents,
  setModifyContents,
  onModifyTodo,
}: PropTypes): JSX.Element => {
  const onCloseModal = useCallback((): void => {
    setIsModal(false);
  }, [setIsModal]);

  const onChange = useCallback((e: ChangeEvent<HTMLInputElement>): void => {
    const { value } = e.target;
    setModifyContents(value);
  }, [setModifyContents]);
  
  return (
    <>
      <div className='TodoModal-Overlay' onClick={onCloseModal}></div>
      <div className='TodoModal'>
        <div className='TodoModal-Title'>
          <div>Todo 수정하기</div>
          <FaPen />
        </div>

        <div className='TodoModal-Contents'>
          <input
            type='text'
            className='TodoModal-Contents-Input'
            value={modifyContents}
            onChange={onChange}
            placeholder='Todo 입력'
          />

          <button
            className='TodoModal-Contents-Button'
            onClick={onModifyTodo}
          >
            수정하기
          </button>
        </div>
      </div>
    </>
  );
};

export default TodoModal;

아래는 TodoModal.scss 파일의 코드입니다.

.TodoModal {
  width: 40%;
  height: 40%;
  background-color: var(--white);
  padding: 1.6rem;
  border-radius: 10px;
  overflow-x: hidden;
  overflow-y: auto;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  display: flex;
  display: -webkit-flex;
  flex-direction: column;
  -ms-flex-direction: column;
  align-items: center;

  &-Overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.4);
    z-index: 2;
  }

  &-Title {
    display: flex;
    display: -webkit-flex;
    align-items: center;
    margin-bottom: 12px;

    & > div {
      font-size: 1.5rem;
      margin-right: 6px;
    }
  }

  &-Contents {
    display: flex;
    display: -webkit-flex;
    flex-grow: 1;
    flex-direction: column;
    -ms-flex-direction: column;
    justify-content: center;
    align-items: center;

    &-Input {
      outline: none;
      border: none;
      border-bottom: 2px solid var(--main);
      margin-bottom: 10px;
      padding: 5px 0;
      font-size: 1.3rem;
    }

    &-Button {
      width: 100%;
      height: 50px;
      outline: none;
      border: none;
      color: var(--white);
      background-color: var(--main);
      font-size: 1.2rem;
      cursor: pointer;
    }
  }
}

위와 같이 모달의 스타일링 작업을 모두 해주었습니다.
이제 조건부에 따라 모달을 렌더링 해주어야 하므로, TodoItem.tsx 파일에서 </> 가 적혀있는 코드 바로 위에다가 아래의 내용을 추가해주세요!

{
  isModal &&
  <TodoModal
    setIsModal={setIsModal}
    modifyContents={modifyContents}
    setModifyContents={setModifyContents}
    onModifyTodo={onModifyTodo}
  />
}

아래의 코드를 추가하고 나서, X버튼 왼쪽에 있는 펜 모양의 아이콘을 눌러보세요! state가 알맞게 동작하면서 모달이 아래와 같이 렌더링 됩니다. 그리고 TodoItem.tsx에서 props로 넘겨주었던 onModify 함수를 이용하여 input의 기본값에 수정할 내용이 오도록 해주었습니다.

그러고나서 수정하기 버튼을 누르면 TodoItem.tsx에서 props로 넘겨주었던 onModifyTodo 함수가 실행되면서 최종적으로 Todo의 값이 업데이트 됩니다. (업데이트가 문제없이 되었다면 성공입니다!)

7. 마치며 🙂

어제 처음으로 Recoil을 이용한 프로젝트 TodoList를 만들어보면서 사용해봤습니다. 제가 기존에 사용하던 Redux나 MobX와 다른 형태의 상태관리 라이브러리여서 사용하는데 헷갈리겠다는 생각도 들었는데, 리액트와 문법 등 유사한점이 많았으며, 리액트 개발자라면 누구나 편리하게 사용할 수 있도록 설계된 좋은 상태관리 라이브러리인 것 같습니다.

나중에 제가 개인 프로젝트를 진행할때도 Recoil을 적극 수용하여 활용하지 않을까 싶기도 합니다. 이상으로 글을 마치겠습니다! 긴 글 읽어주셔서 감사합니다! 😊😊

profile
블로그 이전: https://yiyb-blog.vercel.app

6개의 댓글

comment-user-thumbnail
2021년 5월 7일

덕분에 많이 배웠습니다. 감하십니다.

답글 달기
comment-user-thumbnail
2022년 1월 26일

애증의 리덕스... RTK를 써도 결국 보일러 플레이트 코드가 너무 많아서 불편햇는데, 리코일은 넘나 간편하군요! 덕분에 좋은 기술 알아갑니다~!

답글 달기
comment-user-thumbnail
2022년 6월 30일

많이 배웠습니다 감사합니다!

답글 달기
comment-user-thumbnail
2022년 9월 1일

todoInput도 atom으로 관리하신 이유가 궁금해요! 리코일에선 보통 그렇게 하나요??

답글 달기
comment-user-thumbnail
2022년 10월 24일

atom을 직접 변경할 경우 해당 atom 을 바라보고 있는 모든 컴포넌트 에서 상태가 변경이 되어 예상치 못한 부작용(Side-effects) 이 일어날 수 있는 것도 내용에 추가되면 좋을 것 같습니다

답글 달기
comment-user-thumbnail
2022년 12월 9일

리코일 입문차 예제 코드가 어떻게 흘러가는지 궁금했는데 덕분에 도움이 많이 되었습니다.
확실히 React Hooks랑 문법이 흡사해서 쫙쫙 붙는 느낌이 좋네요.

답글 달기