React | 숙련주차 개인과제 복습

하영·2024년 8월 26일
1

React

목록 보기
12/17

그동안은 파일을 세부적으로 나누지 않고 대부분 한 파일에 다 작업을 했었다. 그래서 코드 위치나 시점을 지금처럼 생각하지 않아도 됐었다.
컴포넌트를 총 6개로 작업하다보니 import해오는것도 신경써야하고 이 이벤트를 어느 컴포넌트에 작성해야할지 꽤나 고생시러웠다😢

크고 작은 에러 (김에러씨) 가 있었지만 그 중에서 카드교체 기능으로 수정하면서 생겼던 오류와 해결했던 과정을 정리해보았다.

🚧 포켓몬사전 코드 수정하기

👩🏻‍💻 구현해야 할 것

  • 초기 화면에는 카드를 선택하지 않은 빈 카드 6개로 만들기
  • 카드 추가를 클릭하면 원래 있던 빈 카드가 클릭한 카드로 교체시키기
  • 이미 추가된 카드를 클릭하거나 / 카드를 7개 선택하면 경고 피드백 보여주기

위 이미지처럼 작동이 되어야하는데 나는 빈 Dashboard에 추가한 카드만 뜨는 방식이었다.

👩🏻‍💻 수정할 것

  • state 초기값을 부여해서 빈 카드 만들기
  • 클릭한 카드로 추가,삭제 되게 만들기

처음 빈 카드를 만들려면 useState의 초기값이 6개 만들어야했다.
어떻게 만들어야할지 고민하다가 null 을 사용해보기로 했다.

01. state 초기값 수정하기

아무것도 선택하지 않은 상태의 카드 = null 로 지정

const INIT_TEXT = [null, null, null, null, null, null];
const [selectedPokemon, setSelectedPokemon] = useState(INIT_TEXT);

// 🍯 약간의 꿀팁
const INIT_TEXT = Array.from({ length: 6 }, () => null);

다소 무식?하게 null을 6번 적었는데 아래 주석처럼 Array.from을 사용해서 가독성이나 유지보수하기 좋은 코드를 작성할 수 있다.

02. 추가버튼 로직 수정하기

const addPokemon = (pokemon) => {
	// 01. 중복체크
    const isAlreadySelected = selectedPokemon.some((item) => {
      return item && item.id === pokemon.id;
    });

  
    // 02. 최대 6개 체크
    const selectedCount = selectedPokemon.filter(
      (item) => item !== null
    ).length;

  
    // 03. 선택된 포켓몬 리스트에 추가
    const firstNullIndex = selectedPokemon.indexOf(null);

    if (firstNullIndex !== -1) {
      const newSelectedPokemon = [...selectedPokemon];
      newSelectedPokemon[firstNullIndex] = pokemon;

      setSelectedPokemon(newSelectedPokemon);
    }
  };

중복체크

const isAlreadySelected = selectedPokemon.some((item) => {
  return item && item.id === pokemon.id;
});

// 초기값 null 주기 전 코드
if (selectedPokemon.length >= 6) {
      alert("최대 6개까지만 선택할 수 있습니다.");
      return;

처음에는 id값만 일치하는지 분별했는데 초기값을 null 로 주면서 null이 아니고, id값도 일치하는가 2번의 검사가 필요하다.


최대 6개 체크

const selectedCount = selectedPokemon.filter(
  (item) => item !== null
).length;

카드 개수가 6개인지 체크하는 부분도 사용자가 추가한 카드는 item이 null이 아닐 때니까 그 값을 가지고 filter를 해주었다.


선택된 포켓몬 리스트에 추가

 const firstNullIndex = selectedPokemon.indexOf(null);

if (firstNullIndex !== -1) {
  const newSelectedPokemon = [...selectedPokemon];
  newSelectedPokemon[firstNullIndex] = pokemon;

  setSelectedPokemon(newSelectedPokemon);
}

추가버튼을 누른 카드가 대쉬보드 쪽으로 교체?되는 부분은 이렇게 작성했다.
먼저 indexOfnull 인지 확인하고 indexOf 출력이 -1 이면 없다는 뜻이니까 -1이 !== 아니라면 기존 selectedPokemon state를 활용해서 추가하는 식으로 작성했다.


03. toast 라이브러리 피드백 ui 수정하기

선택구현사항이었는데 매번 alert만 사용하는게 질려서 새로 바꿔보았다.

https://www.npmjs.com/package/react-toastify

React-Toastify 를 사용했고 css 부분이라 간단하게 기록만 해두려한다.

// 01. toast 라이브러리 import 하기
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

// 02. 경고창 함수 만들기
const notify = (message) =>
    toast(message, {
      position: "top-center",
    });

// 03. notify 함수 적용시키기
if (isAlreadySelected) {
      notify("이미 선택한 포켓몬입니다.");
      return;
    }

if (selectedCount >= 6) {
      notify("최대 6개까지 선택할 수 있습니다.");
      return;
    }

alert 라고 적었던 걸 notify 라고 적은거랑 똑같다.
message 인자를 받아와서 해당 조건에 맞는 메시지가 뜨도록 했다.


👩🏻‍💻 04. 발생한 문제점

순탄하게 넘어가면 내가 아니지... 이렇게 코드를 작성하면서 또 문제가 발생했는데 뭐였냐면 "이미 추가한 카드에서 경고 피드백이 뜨지 않는" 오류가 생겼다.

문제의 코드

const addPokemon = (pokemon) => {
    const isAlreadySelected = selectedPokemon.some((item) => {
      return item && item.id === pokemon.id;
    });

    if (isAlreadySelected) {
      notify("이미 선택한 포켓몬입니다.");
      return;
    }
  };

생각해 본 문제점

  • 아래 조건문들과 시기의 차이인가? 하고 순서를 바꿔보았으나 해결 X
  • ! 연산자로 다 바꿔보기…

맨 처음과 추가된 카드 있을 때의 state 변화 확인하기

  • 초기값을 null 로 주니까 기존 로직으로 하면 null 값을 못 읽어왔음 ⇒ item이 null 인지 판단해서 해결함

state에는 문제가 없으니까 추가버튼 이벤트 함수를 콘솔로 찍어봤다.
이미 추가된 카드를 또 누르면 아예 클릭이벤트가 작동하지 않았다.
반복되어 콘솔이 찍힌다거나 에러화면 조차 뜨지 않았음...
또 이미 선택한 카드라면 false에서 true로 바뀌어야하는데 버튼이 동작하지 않으면서 이것도 보이지 않았다.


🔍 수정한 코드

// PokeList.jsx

return (
  <PokemonCard
    key={pokemon.id}
    pokemon={pokemon}
    onAdd={() => {
              if (!isSelected) { 
                //!! 이미 Context 파일에서 검사해줬는데 또 해버림
                addPokemon(pokemon);
              } 
            }}
    isSelected={isSelected}
    />
        );

문제의 코드는 Context 파일이 아니라 PokeList 컴포넌트에 있었다.
이미 Context 작업을 하면서 addPokemon 로직을 짤 때 조건까지 다 했는데 나는 여기서 또 조건을 걸어버린 것이다.

return (
          <PokemonCard
            key={pokemon.id}
            pokemon={pokemon}
            onAdd={() => {
                addPokemon(pokemon);
            }}
            isSelected={isSelected}
          />
        );

이렇게 바꿔주고 다시 콘솔을 확인해보면~

원하던대로 콘솔에도 나오고 이미 클릭한 카드는 true가 나오는 걸 확인할 수 있었다!

따흑흑 따흑흑 이 화면을 얼마나 보고싶었던지 흑흑흑ㅠㅠㅠ
저번 포스팅에서도 썼지만 정말 시점도 잘 봐야하고 중복된 코드가 없는지도 꼭 확인해야겠다. 항상 빨간색 에러창만 보다가 콘솔에도 안 뜨는 경우를 만나니까 이것대로 고통이었다...
완성된 코드로 오늘 TIL도 끝내야지!


👩🏻‍💻 05. 수정완료 코드

PokemonContext 컴포넌트

const addPokemon = (pokemon) => {
		// 01. 중복체크
    const isAlreadySelected = selectedPokemon.some((item) => {
      return item && item.id === pokemon.id;
    });

    if (isAlreadySelected) {
      notify("이미 선택한 포켓몬입니다.");
      return;
    }

    // 02. 최대 6개 체크
    const selectedCount = selectedPokemon.filter(
      (item) => item !== null
    ).length;

    if (selectedCount >= 6) {
      notify("최대 6개까지 선택할 수 있습니다.");
      return;
    }

    // 03. 선택된 포켓몬 리스트에 추가
    const firstNullIndex = selectedPokemon.indexOf(null);

    if (firstNullIndex !== -1) {
      const newSelectedPokemon = [...selectedPokemon];
      newSelectedPokemon[firstNullIndex] = pokemon;

      setSelectedPokemon(newSelectedPokemon);
    }
  };

PokeList 컴포넌트

import styled from "styled-components";
import PokemonCard from "../components/PokemonCard";
import { useContext } from "react";
import { PokemonContext } from "/src/context/PokemonContext.jsx";

const PokemonList = ({ pokemonList }) => {
  const { addPokemon, selectedPokemon } = useContext(PokemonContext);

  return (
    <ListContainer>
      {pokemonList.map((pokemon) => {
        const isSelected = selectedPokemon.some(
          (selected) => selected && selected.id === pokemon.id
        );

        return (
          <PokemonCard
            key={pokemon.id}
            pokemon={pokemon}
            onAdd={() => {
              addPokemon(pokemon);
            }}
            isSelected={isSelected} 
          />
        );
      })}
    </ListContainer>
  );
};

export default PokemonList;
profile
왕쪼랩 탈출 목표자의 코딩 공부기록

2개의 댓글

comment-user-thumbnail
2024년 8월 26일

에러왕 김에러

1개의 답글