본문 바로가기
Frontend/React

[React] 포켓몬도감 만들기5

by 디스코비스킷 2023. 12. 8.
반응형

11. Damage Relations 컴포넌트 생성하기

데미지 관계 데이터

포켓몬의 타입이 어떤 타입에 강한지, 약한지 관계를 표현하는 부분
앞서 받아온 DamageRelations데이터를 살펴보자.

console.log(pokemon?.DamageRelations);

한 포켓몬이 가진 타입에 따라서
더블데미지를 주는 타입(double damage to), 더블데미지를 받는 타입(double damage from),
하프데미지를 주는 타입(half damage to), 하프데미지를 받는 타입(half damage from),
데미지를 안주는 타입(no damage to), 데미지를 안받는 타입(no damage from)
여섯가지 관계를 시각화한다.

UI에 넣어주기 위한 가공된 데이터 최종 형태

가공 첫단계: from과 to로 나누기

import React, { useEffect } from 'react'

const DamageRelations = ({ damages }) => {
  // console.log(damages)
  useEffect(() => {
    const arrayDamage = damages.map((damage) => 
      separateObjectBetweenToAndFrom(damage)
    )
  }, [])

  // from, to로 된것 각 필터링해서 담아줌
  const separateObjectBetweenToAndFrom = (damage) => {
    const from = filterDamageRelations('_from', damage);
    const to = filterDamageRelations('_to', damage);

    console.log("from", from)
    console.log("from", to)
    return {from, to};
  }

  const filterDamageRelations = (valueFilter, damage) => {
    // console.log("entries:",Object.entries(damage));
    const result = Object.entries(damage)
      .filter(([keyName, value]) => {
        return keyName.includes(valueFilter); // _to와 _from으로 나눔
      })
      .reduce((acc, [keyName, value]) => { // reduce는 return해서 acc 메소드에 하나씩 쌓아줌
        // console.log(acc, [keyName, value]);
        const keyWithValueFilterRemove = keyName.replace(
          valueFilter,
          ''
        )
        return (acc = { [keyWithValueFilterRemove]: value, ...acc });
      }, {}) // acc의 초기값 빈객체
      return result;
  }

  return (
    <div>DamageRelations</div>
  )
}

export default DamageRelations

가공 두번째단계: 영향을 받는것만 남기기(from만 남기고 타입 두개일때 합치기)

타입이 두개면 from이 두개가 남는다.
grass속성의 더블데미지가 두개라면 x2가 아니라 x4로 합쳐주어야한다.
*half데미지가 두개라면 1/4배가된다. *

useEffect안에 2개일 경우와 1개일경우 나누기

1개일 경우 데미지값 넣는 함수 적용

  useEffect(() => {
    const arrayDamage = damages.map((damage) =>
      separateObjectBetweenToAndFrom(damage)
    );

    if (arrayDamage.length === 2) {
      // 합치는 부분
    } else {
      postDamageValue(arrayDamage[0].from); // 첫번째 from
    }
  }, []);

postDamageValue: 데미지값 넣는 함수

  // 2. 영향을 받는것만 남기기(from만 남기고 타입 두개일때 합치기)
  const postDamageValue = (props) => {
    const result = Object.entries(props).reduce((acc, [keyName, value]) => {
      const key = keyName;
      const valueOfKeyName = {
        double_damage: "2x",
        half_damage: "1/2x",
        no_damage: "0x",
      };

      // 데미지값 각 객체마다 넣기
      return (acc = {
        [keyName]: value.map((i) => ({
          damageValue: valueOfKeyName[key],
          ...i,
        })),
        ...acc
      });
    }, {});

    console.log(result);
  };

12. 타입이 두 개일 때 Damage Relation 데이터 생성하기

타입이 두개일때 데미지관계 합치기

  useEffect(() => {
    const arrayDamage = damages.map((damage) =>
      separateObjectBetweenToAndFrom(damage)
    );

    if (arrayDamage.length === 2) {
      // 합치는 부분
      const obj = joinDamageRelations(arrayDamage);
      // console.log(reduceDuplicateValues(postDamageValue(obj.from)));
      console.log(reduceDuplicateValues(postDamageValue(obj.from)))
      // reduceDuplicateValues(obj)
    } else {
      postDamageValue(arrayDamage[0].from); // 첫번째 from
    }
  }, []);
  // 2-2. 두개일때: 합치기
  const joinDamageRelations = (props) => {
    return {
      to: joinObjects(props, 'to'),
      from: joinObjects(props, 'from'),
    }
  }

  // (합치기 메서드)
  const joinObjects = (props, string) => {
    const key = string;
    const firstArrayValue = props[0][key];
    const secondArrayValue = props[1][key];

    // firstArrayValue에 secondArrayValue를 합치기
    const result = Object.entries(secondArrayValue).reduce(
      (acc, [keyName, value]) => {
        // console.log("ddd", acc, [keyName, value]);
        const result = firstArrayValue[keyName].concat(value); // 첫번째 array value에 해당하는 데미지에 합침
        return (acc = { [keyName]: result, ...acc });
      },
      {}
    );
    return result;
  }

  // 중복 속성 데미지값 배로 처리
  const reduceDuplicateValues = (props) => {
    const duplicateValues = {
      double_damage: "4x",
      half_damage: "1/4x",
      no_damage: "0x",
    };
    // console.log(props)

    // console.log("object", Object.entries(props));
    return Object.entries(props)
      .reduce((acc, [keyName, value]) => {
        const key = keyName;
        // console.log("키밸류",[keyName, value]);
        const verifiedValue = filterForUniqueValues(
            value, 
            duplicateValues[key]
          );

        return (acc = {
          [keyName]: verifiedValue,
          ...acc,
        });
      }, {});
  }

  // 중복부분 데미지밸류 수정
  const filterForUniqueValues = (valueForFiltering, damageValue) => {
    return valueForFiltering.reduce((acc, currentValue) => {
      const {url, name} = currentValue;
      const filterACC = acc.filter((a) => a.name !== name); // name이 같지 않은것만 넣어두기

      return filterACC.length === acc.length 
      ? (acc = [currentValue, ...acc]) // 변경 없으면(중복없으면) currentvalue그대로 넣기
      : (acc = [{damageValue: damageValue, name, url}, filterACC]) // 같은것은 데미지밸류 변경
    }, []);
  }

데이터 정리한것을 스테이트로 정의

  const [damagePokemonForm, setDamagePokemonForm] = useState();

  useEffect(() => {
    const arrayDamage = damages.map((damage) =>
      separateObjectBetweenToAndFrom(damage)
    );

    if (arrayDamage.length === 2) {
      // 합치는 부분
      const obj = joinDamageRelations(arrayDamage);
      setDamagePokemonForm(reduceDuplicateValues(postDamageValue(obj.from)));
    } else {
      setDamagePokemonForm(postDamageValue(arrayDamage[0].from)); // 첫번째 from
    }
  }, []);

13. Damage Relations UI 생성하기

  return (
    <div className="flex gap-2 flex-col">
      {damagePokemonForm ? (
        <>
          {Object.entries(damagePokemonForm)
            .map(([keyName, value])=> {
              const key = keyName;
              const valuesOfKeyName = {
                double_damage: 'Weak',
                half_damage: 'Resistant',
                no_damage: 'Immune'
              }

              return (
                <div key={key}>
                  <h3 className="capitalize font-medium text-sm md:text-base text-slate-500 text-center">
                    {valuesOfKeyName[key]}
                  </h3>
                  <div className="flex flex-wrap gap-1 justify-center">
                    {value.length > 0 ? (
                      value.map(({ name, url, damageValue }) => (
                        <Type 
                          key={url}
                          type={name}
                          damageValue={damageValue}
                        />
                      ))
                    ) : (
                      <Type key={"none"} type={'none'} />
                    )}
                  </div>
                </div>
              )
            })

          }
        </>
      ):(
        <div>dd</div>
      )}
    </div>
  );


강의대로 작성하고
key값 다 있는거 확인햇는데 어디가 문제인지 모르겠다!!

14. Damage Relation 모달 생성하기

DetailPage > index.jsx에서 모달 state생성

  const [isModalOpen, setIsModalOpen] = useState(false);
...

return (
...
      {isModalOpen && (
        <DamageModal setIsModalOpen={setIsModalOpen} damages={pokemon.DamageRelations} />
      )}
...
)

DamageModal 생성

import React from 'react'
import DamageRelations from './DamageRelations'

const DamageModal = ({ setIsModalOpen, damages }) => {
  return (
    <div className="flex items-center justify-center z-40 fixed left-0 bottom-0 w-full h-full bg-gray-8">
      <div className="modal bg-white rounded-lg w-1/2">
        <div className="flex flex-col items-start p-4">
          <div className="flex items-center w-full justify-between">
            <div className="text-gray-900 font-medium text-lg">데미지 관계</div>
            <span
              onClick={() => setIsModalOpen(false)}
              className="text-gray-900 font-medium text-lg cursor-pointer"
            >
              X
            </span>
          </div>

          <DamageRelations damages={damages} />
        </div>
      </div>
    </div>
  );
};

export default DamageModal

damages 내려주는거 체크하고 props도 같이 체크한다.

15. 모달 창 외부 클릭 시 모달 닫게 만드는 Custom Hooks 생성하기

기능 기획

어디를 클릭하는지 구분 (모달창 밖)
react hook 생성
모달창 밖을 클릭하면 Callback 함수를 호출하는 리스너를 등록해주기
Callback함수 안에서 모달 닫아주기

useRef라는 것을 이용해서 구분할 수 있다.

useRef란?

  1. 변수관리

2. 특정 DOM을 선택
할때 사용하는 React Hooks이다.

onClickOutside Hooks생성

import { useEffect } from "react";


const onClickOutside = (ref, handler) => {
    useEffect(() => {
        // listner
        const listner = (event) => {
            console.log(event.target)
            // 안을 클릭하면 return, 밖 클릭시 handler 호출
            if(!ref.current || ref.current.contains(event.target)) {
                // listner에서 event받아옴
                // 클릭하는 타겟이 ref.current안에 포함되는지(안을 클릭했는지)
                return;
            }
            handler()
        };

        document.addEventListener('mousedown', listner);
        return () => {
            document.addEventListener("mousedown", listner);
        }
    }, [ref, handler])
}

export default onClickOutside;

데미지모달 ref 등록

  const ref = useRef();
    useOnclickOutside(ref, () => setIsModalOpen(false));
...
      <div ref={ref} className="modal bg-white rounded-lg w-1/2">

모달요소에 ref등록을 하고
Hooks를 호출한다.

반응형

'Frontend > React' 카테고리의 다른 글

[React] 포켓몬도감 만들기7  (0) 2023.12.08
[React] 포켓몬도감 만들기6  (1) 2023.12.08
[React] 포켓몬도감 만들기4  (0) 2023.12.08
[React] 포켓몬도감 만들기3  (1) 2023.12.08
[React] 포켓몬도감 만들기2  (0) 2023.12.08

최근댓글

최근글

© Copyright 2023 jngmnj