모달 닫고 열기 + 게시글 수정, 삭제(현 프로젝트: 단어 수정) 시 index값으로 각각 맞는 데이터 찾기

개발공부·2022년 11월 8일
0

* 문제점

  1. (모달) 수정화면을 위해 모달창이 켜지고 꺼지는 것이 오류 발생
    ▶ modal 창 여는 버튼과 닫는 버튼에 각각 true, false 표시

  2. (객체, 배열)타입을 3가지로 나눠 화면에 표시, 수정과 삭제에 문제 생김
    ▶ 수정, 삭제 관련해 배열을 따로 뒀으나, index 사용을 위해 easy, middle, advance로 타입 나눈 것을 한 객체(wordLists)로 통일

  3. (useEffect) 관련 내용 정리

* 참고한 블로그 및 사이트

* 배운 것

  • 객체와 배열에 관해 공부(참고 : tcpschool.com)

객체 : 이름과 값으로 구성된 프로퍼티의 정렬되지 않은 값
프로퍼티가 함수로 올 경우 메소드라고 함

배열 : map으로 이루어진, 순서가 있는 집합

▶아래의 예시는 배열 안에 객체를 넣은 것

  wordLists: [
    {
      id: 2,
      english: "red",
      korean: "빨강",
      type: "easy",
    },
    {
      id: 4,
      english: "blue",
      korean: "파랑",
      type: "easy",
    },
     {
      id: 6,
      english: "yellow",
      korean: "노랑",
      type: "middle",
    },
  ]
  • splice, filter와 map에 대해 공부

splice : 배열.splice(삭제하려고 하는 위치, 삭제하려는 수, 넣을 값)
▶ 기존 배열에 영향 있음

// splice 예시

var test = [
    {id: 1, name: 'a'},
     {id: 2, name: 'b'},
     {id: 3, name: 'c'},
     {id: 4, name: 'd'},
    ]
 
test.splice(2, 1, {id: 3, name: "test"}) // {id: 3, name: 'c'}

console.log(test) 
// {id: 1, name: 'a'}
// {id: 2, name: 'b'}
// {id: 3, name: 'test'}
// {id: 4, name: 'd'}

filter : 요소를 걸러내어 배열로 true/false 반환, 없으면 빈 배열, 조건을 줄 수 있음
▶ 기존 배열에 영향 없음

// filter 예시 

var test = [
    {id: 1, name: 'a'},
     {id: 2, name: 'b'},
     {id: 3, name: 'c'},
     {id: 4, name: 'd'},
    ]
test.filter((t) => t.id > 1)
//{id: 2, name: 'b'},
//{id: 3, name: 'c'},
//{id: 4, name: 'd'},

console.log(test) 
// {id: 1, name: 'a'}
// {id: 2, name: 'b'}
// {id: 3, name: 'test'}
// {id: 4, name: 'd'}

map : 요소를 일괄적으로 변경
▶ 기존 배열에 영향 없음

//map 예시
var test = [
    {id: 1, name: 'a'},
     {id: 2, name: 'b'},
     {id: 3, name: 'c'},
     {id: 4, name: 'd'},
    ]
test.map((t, i) => t.id) //t는 test 안의 객체, i는 index
//[1, 2, 3, 4]

test.map((t, i) => t.name)
//['a', 'b', 'c', 'd']

console.log(test)
// {id: 1, name: 'a'}
// {id: 2, name: 'b'}
// {id: 3, name: 'test'}
// {id: 4, name: 'd'}

* 수정 결과

▶ 수정 버튼 누름

▶ 수정 하기 전 단어 화면에 나옴

▶ 영어, 한글, 타입(easy -> middle)로 변경

▶ 수정 버튼 누르면 easy에서 middle로 변경됨

* 삭제 결과

▶ 수정한 값을 삭제 버튼을 누름

▶ 취소 누르면 다시 원래 화면으로 돌아옴

▶ 삭제 버튼 누르면 삭제됨

▶ 화면에서 삭제됨(실제 데이터도 삭제됨)

* 코드 관련

1. (모달)

[WordList.js]

const WordList = () => {
  const [modal, setModal] = useState(false); //수정 모달
  const [removeModal, setRemoveModal] = useState(false); //삭제 모달
  
 const onReviseWord = (e) => {
    setModal(true);
  };

  const onRemoveWord = (e) => {
    setRemoveModal(true);
  };

  return (
  <>
   {/* 수정 모달창 : true일 때 열리고 false일 때 안 보임 */}
      {modal ? <ReviseWordModal isId={id} setModal={setModal} /> : null}
      {/* 삭제 모달창 : true일 때 열리고 false일 때 안 보임 */}
      {removeModal ? (
        <RemoveWordModal isId={id} setRemoveModal={setRemoveModal} />
      ) : null}
  </>
  )
  export default wordList;
  

[ReviseWordModal.js]
▶ 삭제도 동일함
▶ 모달과 관련된 css는 tailwindcss 적용

import { Fragment, useRef, useState } from "react";
import { Dialog, Transition, Listbox } from "@headlessui/react";
import { useSelector, useDispatch } from "react-redux";
import {
  ArrowsRightLeftIcon,
  ArrowDownIcon,
  CheckIcon,
} from "@heroicons/react/24/outline";

const ReviseWordModal = ({ isId, setModal }) => { //보내온 데이터 받음

  const [open, setOpen] = useState(true);
  const [selected, setSelected] = useState(typesName[0]);

  const onReviseWordSubmit = () => {
    setModal(false); //true -> false로 바꿀 것
  };

  const onOpenCloseModal = () => {
    console.log("open", open);
    setModal(false); //true -> false로 바꿀 것
  };
  const cancelButtonRef = useRef(null);

  return (
    <Transition.Root show={open} as={Fragment}>
      <Dialog
        as="div"
        className="relative z-10"
        initialFocus={cancelButtonRef}
        onClose={setOpen}
      >
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
        </Transition.Child>

        <div className="fixed inset-0 z-10 overflow-y-auto">
          <div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
              enterTo="opacity-100 translate-y-0 sm:scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 translate-y-0 sm:scale-100"
              leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
            >
              <Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg">
                <div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
                  <div className="sm:flex sm:items-start">
                    <div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-light-green sm:mx-0 sm:h-10 sm:w-10">
                      <ArrowsRightLeftIcon
                        className="h-6 w-6 text-white"
                        aria-hidden="true"
                      />
                    </div>
                    <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
                      <Dialog.Title
                        as="h3"
                        className="text-lg font-medium leading-6 text-gray-900"
                      >
                        단어 수정
                      </Dialog.Title>
                      <div className="flex w-96">
                        <div>
                          <input
                            onChange={onChangeEnglish}
                            placeholder={wordLists[isId].english}
                            // placeholder="englsih"
                            type="text"
                            name="english"
                            className="sm:600 w-52 grid grid-cols-2 gap-4 place-content-center
                          pl-2 h-9  placeholder:italic placeholder:text-slate-400 flex items-start bg-white border-solid border-2 border-light-green group-hover:opacity-80 rounded-full m-2"
                          />
                          <input
                            onChange={onChangeKorean}
                            placeholder={wordLists[isId].korean}
                            // placeholder="korean"
                            type="text"
                            name="korean"
                            className="sm:600 w-52 grid grid-cols-2 gap-4 place-content-center
                          pl-2 h-9  placeholder:italic placeholder:text-slate-400 flex items-start bg-white border-solid border-2 border-light-green group-hover:opacity-80 rounded-full m-2"
                          />
                        </div>
                        <div className="ml-10 mt-2">
                          <Listbox value={selected} onChange={setSelected}>
                            <div className="relative ">
                              <Listbox.Button className="relative w-full cursor-default rounded-lg bg-white py-2 pl-2 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm border-2 border-light-green">
                                <span className="block">{selected.name}</span>
                                <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
                                  <ArrowDownIcon
                                    className="h-5 w-5 text-gray-400"
                                    aria-hidden="true"
                                  />
                                </span>
                              </Listbox.Button>
                              <Transition
                                as={Fragment}
                                leave="transition ease-in duration-100"
                                leaveFrom="opacity-100"
                                leaveTo="opacity-0"
                              >
                                <Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
                                  {typesName.map((t, typeIdx) => (
                                    <Listbox.Option
                                      key={typeIdx + 1}
                                      className={({ active }) =>
                                        `relative cursor-default select-none py-2 pl-2 pr-4 ${
                                          active
                                            ? "bg-light-orange rounded-lg text-black"
                                            : "text-gray-900"
                                        }`
                                      }
                                      value={t}
                                    >
                                      {({ selected }) => (
                                        <>
                                          <span
                                            className={`block ${
                                              selected
                                                ? "font-medium"
                                                : "font-normal"
                                            }`}
                                            name={t.name}
                                          >
                                            {t.name}
                                          </span>
                                          {selected ? (
                                            <span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
                                              <CheckIcon
                                                className="h-5 w-5 ml-11"
                                                aria-hidden="false"
                                              />
                                            </span>
                                          ) : null}
                                        </>
                                      )}
                                    </Listbox.Option>
                                  ))}
                                </Listbox.Options>
                              </Transition>
                            </div>
                          </Listbox>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
                <div className="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
                  <button
                    type="button"
                    className="inline-flex w-full justify-center rounded-md border border-transparent bg-light-green px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-dark-green focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
                    onClick={onReviseWordSubmit}
                  >
                    수정
                  </button>
                  <button
                    type="button"
                    className="mt-3 inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-base font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
                    onClick={onOpenCloseModal}
                    ref={cancelButtonRef}
                  >
                    취소
                  </button>
                </div>
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition.Root>
  );
};

export default ReviseWordModal;

2. (객체, 배열)

▶ 타입 따라 결과창을 나둠(easy, middle, advance)
▶ wordLists와 easyLists, middleLists, advanceLists 만듦
▶ 문제점 : 데이터 수정, 삭제 시 index값 필요할 때 문제 생김

[wordLists 데이터]

 wordLists: [
    {
      id: 2,
      english: "red",
      korean: "빨강",
      type: "easy",
    },
    {
      id: 4,
      english: "blue",
      korean: "파랑",
      type: "easy",
    },
    {
      id: 6,
      english: "yellow",
      korean: "노랑",
      type: "middle",
    },
     {
      id: 8,
      english: "green",
      korean: "초록",
      type: "advance",
    },
 ]

[wordLists와 easyLists, middleLists, advanceLists]
▶ filter 적용했었음
wordLists와 나머지 filter가 적용된 것들의 인덱스가 다름
▶ 수정, 삭제 시 array.splice 이용(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)

splice() 메서드 : 배열의 기존 요소 삭제나 교체 할 때 사용(인덱스 값 이용)

  • 문제 있던 코드
import { useSelector } from "react-redux";

//redux-toolkit으로 만든 wordSlice에서 가져옴

 const { wordLists } = useSelector((state) => state.word); //id: 2, 4, 6, 8(4개, 인덱스 0, 1, 2, 3)
 const easyLists = wordLists.filter((word) => word.type ==='easy') //id: 2, 4(2개, 인덱스 0, 1)
 const middleLists = wordLists.filter((word) => word.type ==='middle') //id: 6(1개, 인덱스 0)
 const advanceLists = wordLists.filter((word) => word.type ==='advance') //id: 8(1개, 인덱스 0)
  • 수정한 코드
    ▶ map 함수로 반복할 때 if문 적용
    ▶ return 안 넣으면 값이 제대로 출력되지 않음
 {wordLists.map((word, index) => {
                if (word.type === "easy") {
                  return (
                    <>
                    ...
                    </>
                  );
                }
              })}

3. (useEffect) 관련 내용 정리

▶ 참고 : https://www.youtube.com/watch?v=pvTuVXlrGUY
▶ 컴포넌트가 리렌더링 될 때마다 리액트에게 어떤 일을 실행시켜달라 요청 가능
▶ 특정 state가 변경될 때만 실행되게 하고 싶음([]에 특정 state를 넣기)

▶ 첫 렌더링 이후에 이후에는 실행시키고 싶지 않을 때
두 번째 array([])를 빈칸으로 남겨두면 됨

import React, {useEffect} from "react"
useEffect(() => {
}, [])

profile
개발 블로그, 티스토리(https://ba-gotocode131.tistory.com/)로 갈아탐

1개의 댓글

comment-user-thumbnail
2023년 8월 8일

.

답글 달기