포스트

Effic 개발일지 - Day 9

Effic 개발 9일 차

참고

  • swiper React 사용
    • 공식문서 : https://swiperjs.com/react#usage
    • 블로그 : https://xionwcfm.tistory.com/331

할 일

  • MyTodo 구현
    • 컴포넌트 작성
    • zustand→ todoStore → 갱신 및 가져오기
  • MyNote 구현
    • 컴포넌트 작성
    • zustand → noteStore → 수정, 삭제, 추가

MyNote 구현

컴포넌트

MyNote컴포넌트.png

컴포넌트 트리

  • MyNote.tsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
      import React, { memo } from "react";
      import { useNoteStore } from "../../../../store/noteStore";
      import { AddNoteButton, NoteElement } from "..";
      import { areaTitle } from "../../../../style.css";
      import { addButtonArea, noteElementWrapper } from "./MyNote.css";
      import ModalPortal from "../../../../share/ui/Modal/ModalPortal";
      import NoteInfoModal from "../NoteInfoModal/NoteInfoModal";
        
      const MyNote: React.FC = () => {
        const notes = useNoteStore((state) => state.notes);
        const isNewNote = useNoteStore((state) => state.isNew);
        const activeNote = useNoteStore((state) => state.activeNote);
        return (
          <>
            <ModalPortal>
              <NoteInfoModal isNew={isNewNote} activeNote={activeNote} />
            </ModalPortal>
            <h3 className={areaTitle}>나의 메모</h3>
            <div className={addButtonArea}>
              <AddNoteButton />
            </div>
            <div className={noteElementWrapper}>
              {notes &&
                notes.map((note) => (
                  <NoteElement key={note.id} id={note.id} title={note.title} />
                ))}
            </div>
          </>
        );
      };
        
      export default memo(MyNote);
        
    
    • NoteElement

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      
        import React, { memo } from "react";
        import { TNote } from "../../../../share/types";
        import {
          buttonArea,
          infoButton,
          noteElementWrapper,
          removeButton,
        } from "./NoteElement.css";
        import { MdInfo } from "react-icons/md";
        import { FaRegTrashAlt } from "react-icons/fa";
        import { useNoteStore } from "../../../../store/noteStore";
        import { button } from "../../../../style.css";
              
        type TNoteElemntProps = Pick<TNote, "id" | "title">;
              
        const NoteElement: React.FC<TNoteElemntProps> = ({ id, title }) => {
          const setActiveNoteAction = useNoteStore((state) => state.setActiveNote);
          const noteInfoModalOpen = useNoteStore((state) => state.noteModalOpen);
          const setIsExistNote = useNoteStore((state) => state.setIsExist);
              
          const removeNote = useNoteStore((state) => state.removeNote);
              
          const handleInfoButton = () => {
            setActiveNoteAction(id);
            noteInfoModalOpen();
            setIsExistNote();
          };
              
          const handleRemoveNote = () => {
            removeNote(id);
          };
              
          return (
            <div className={noteElementWrapper}>
              <div>{title}</div>
              <div className={buttonArea}>
                <button
                  onClick={handleInfoButton}
                  className={`${button} ${infoButton}`}
                >
                  <MdInfo />
                </button>
                <button
                  onClick={handleRemoveNote}
                  className={`${button} ${removeButton}`}
                >
                  <FaRegTrashAlt />
                </button>
              </div>
            </div>
          );
        };
              
        export default memo(NoteElement);
              
      

noteStore 구현 : zustand

  • noteStore.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    
      import { v4 } from "uuid";
      import { create } from "zustand";
      import { devtools } from "zustand/middleware";
      import { immer } from "zustand/middleware/immer";
      import { getCurrentDate } from "../share/utils/func.ts";
        
      import { TNote } from "../share/types/index.ts";
        
      interface INoteState {
        author: string;
        notes: TNote[];
        activeNote: TNote;
        isNew: boolean;
        noteModalisOpen: boolean;
      }
        
      interface INoteAction {
        addNote: ({ title, content }: { title: string; content: string }) => void;
        removeNote: (noteId: string) => void;
        setActiveNote: (noteId: string) => void;
        updateNote: (
          noteId: string,
          updateTitle: string,
          updateContent: string
        ) => void;
        setIsNew: () => void;
        setIsExist: () => void;
        noteModalOpen: () => void;
        noteModalClose: () => void;
      }
        
      const initialNoteState: TNote[] = [...];
        
      export const useNoteStore = create<INoteState & INoteAction>()(
        devtools(
          immer((set) => ({
            author: "user",
            isNew: false,
            notes: initialNoteState,
            activeNote: initialNoteState[0],
            noteModalisOpen: false,
            addNote: ({ title, content }: { title: string; content: string }) =>
              set((state: INoteState) => {
                state.notes.push({
                  id: v4(),
                  content,
                  title,
                  date: getCurrentDate(),
                });
              }),
            removeNote: (noteId: string) =>
              set((state: INoteState) => {
                state.notes = state.notes.filter((note) => note.id !== noteId);
              }),
            setActiveNote: (noteId: string) =>
              set((state: INoteState) => {
                state.activeNote = state.notes.find((note) => note.id === noteId)!;
              }),
            updateNote: (
              noteId: string,
              updateTitle: string,
              updateContent: string
            ) =>
              set((state: INoteState) => {
                state.notes = state.notes.map((note) =>
                  note.id !== noteId
                    ? note
                    : {
                        ...note,
                        title: updateTitle,
                        content: updateContent,
                      }
                );
              }),
            setIsNew: () =>
              set((state: INoteState) => {
                state.isNew = true;
              }),
            setIsExist: () =>
              set((state: INoteState) => {
                state.isNew = false;
              }),
            noteModalOpen: () =>
              set((state: INoteState) => {
                state.noteModalisOpen = true;
              }),
            noteModalClose: () =>
              set((state: INoteState) => {
                state.noteModalisOpen = false;
              }),
          })),
          { name: "noteStore" }
        )
      );
        
    

MyTodo 구현

컴포넌트

MyTodo컴포넌트

컴포넌트 트리

  • MyTodo.tsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
      import React, { memo } from "react";
      import Todos from "./Todos/Todos";
      import { areaTitle } from "../../../../style.css";
      import { myTodoWrapper, todoCardWrapper } from "./MyTodo.css";
        
      const MyTodo: React.FC = () => {
        return (
          <>
            <div className={myTodoWrapper}>
              <h3 className={areaTitle}>나의 할 일</h3>
              {/* <TodoCard /> */}
              <div className={todoCardWrapper}>
                <Todos />
              </div>
            </div>
          </>
        );
      };
        
      export default memo(MyTodo);
        
    
    • Todos.tsx

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      
        import React from "react";
        import { Swiper, SwiperSlide } from "swiper/react";
        import TodoCard from "../../TodoCard/TodoCard";
        import { useTodoStore } from "../../../../../store/todoStore";
        import { Navigation, Pagination } from "swiper/modules";
        import { swiperWrapper } from "./Todos.css";
              
        const Todos: React.FC = () => {
          const todos = useTodoStore((state) => state.todos);
          return (
            <div className={swiperWrapper}>
              <Swiper
                spaceBetween={30}
                slidesPerView={4}
                centeredSlides={true}
                autoplay={false}
                pagination=
                rewind={true}
                freeMode={true}
                scrollbar=
                modules={[Pagination, Navigation]}
                navigation={true}
              >
                {todos &&
                  todos.map((todo) => (
                    <SwiperSlide key={todo.id}>
                      <TodoCard
                        // key={todo.id}
                        content={todo.content}
                        id={todo.id}
                        priority={todo.priority}
                      />
                    </SwiperSlide>
                  ))}
              </Swiper>
            </div>
          );
        };
              
        export default Todos;
              
      
      • TodoCard.tsx

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        
          import React from "react";
          import {
            contentArea,
            firstColors,
            secondColors,
            thirdColors,
            todoCardContent,
          } from "./TodoCard.css";
          import { useTodoStore } from "../../../../store/todoStore";
                    
          type TTodoCardProps = {
            id: string;
            content: string;
            priority: number;
          };
                    
          const TodoCard: React.FC<TTodoCardProps> = ({ id, content, priority }) => {
            const setActiveTodo = useTodoStore((state) => state.setActiveTodo);
                    
            return (
              <div
                className={`${todoCardContent} ${
                  priority === 1
                    ? firstColors
                    : priority === 2
                    ? secondColors
                    : thirdColors
                }`}
                onClick={() => setActiveTodo(id)}
              >
                <div className={contentArea}>{content}</div>
              </div>
            );
          };
                    
          export default TodoCard;
                    
        

todoStore 구현 : zustand

  • todoStore.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    
      import { v4 } from "uuid";
      import { create } from "zustand";
      import { devtools } from "zustand/middleware";
      import { immer } from "zustand/middleware/immer";
      import { getCurrentDate } from "../share/utils/func";
        
      export interface ITodo {
        id: string;
        content: string;
        priority: number;
        done: boolean;
      }
        
      interface ITodoStore {
        todos: ITodo[];
        todoModalIsOpen: boolean;
        activeTodo: ITodo;
        addTodo: (todo: ITodo) => void;
        todoModalOpenAction: () => void;
        todoModalCloseAction: () => void;
        setActiveTodo: (todoId: string) => void;
      }
        
      const initialTodos: ITodo[] = [...];
        
      export const useTodoStore = create<ITodoStore>()(
        devtools(
          immer((set) => ({
            todos: initialTodos,
            activeTodo: initialTodos[0],
            todoModalIsOpen: false,
            addTodo: (todo: ITodo) =>
              set((state: ITodoStore) => {
                state.todos.push(todo);
              }),
            toggleTodoDone: (todoId: string) =>
              set((state: ITodoStore) => {
                state.todos = state.todos.map((todo: ITodo) =>
                  todo.id === todoId ? { ...todo, done: !todo.done } : todo
                );
              }),
            setActiveTodo: (todoId: string) =>
              set((state: ITodoStore) => {
                state.activeTodo = state.todos.find((todo) => todo.id === todoId)!;
              }),
            todoModalOpenAction: () =>
              set((state: ITodoStore) => {
                state.todoModalIsOpen = true;
              }),
            todoModalCloseAction: () =>
              set((state: ITodoStore) => {
                state.todoModalIsOpen = false;
              }),
          })),
          { content: "todoStore" }
        )
      );
        
    
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.