프로젝트/올림픽 메달 집계 사이트(完)

[올림픽 메달 집계 사이트] 2. 정렬 , 로컬스토리지로 관리

민ズl 2024. 10. 30. 21:21

1. 정렬

현재는 금메달갯수를 기준으로 정렬했는데, 사용자가 select태그의 option을 클릭한 값에 따라 정렬가능하게 구현하게씀!

먼저 구조부터 짰는데 이때 리액트에서 예상치 못한 오류가!

<select name="medal-filter" id="medalFilter">
  <option value="gold" selected>
    gold
  </option>
  ...
</select>

 

option태그에 selected를 넣으면 아래와 같이 오류가 뜬다👻

오류내용에 해답이 있듯이 `defaultValue` or `value`을 쓰면 됨!

그래서 defaultValue로 바꾸면 에러해결💫

<option value="gold" defaultValue>

 

1-1. 정렬할 필터값들을 배열로 저장한다음 map으로 뿌리기

const filterList = ["gold", "silver", "bronze", "total"];
<select
  name="medal-filter"
  id="medalFilter"
  onChange={handleSelect}
  value={selected}
>
  {filterList.map((item, i) => (
    <option key={i} value={item}>
      {item}
    </option>
  ))}
</select>

 

1-2. 그리고 선택한 필터를 useState에 관리!

(초기값은 금메달 갯수로)

const [selected, setSelected] = useState("gold");

 

1-3. 이제 선택한 필터로 data를 정렬하기!

여기서 엄청 헤맸다,,,ㅠㅠㅠ 결국 gpt 힌트를 받음ㅠㅠㅠㅠ

*객체의 속성 접근 방법

  • 점 표기법
    • obj.key
    • obj라는 객체에서 key라는 이름의 속성을 찾는 방법
    • 속성 이름이 고정되어 있을 때 사용
  • 대괄호 표기법
    • obj["key"]
    • obj라는 객체에서 key라는 이름의 속성을 찾는 방법으로, 키를 문자열로 주는 방법
    • 동적으로 속성 이름을 지정할 때 사용

👻그렇다,,, 나는 점 표기법으로 하니까 작동이 안됐다ㅠㅠㅠ 이걸 몇십분동안 헤맴...ㅠㅠㅠㅠ

const sortedItems = [...medalItems].sort((a, b) => {
      return b.selected - a.selected;
});

 

💫대괄호 표기법으로 바꾸니까 해결!! 그리고 중간에 return을 깜빡하고 안썻더니 또 구현이 안됨☠️

const sortedItems = [...medalItems].sort((a, b) => {
      return b[selected] - a[selected];
});

 

3-2 useEffect로 위에 코드를 넣어주기

여기서 왜 useEffect 훅을 사용할까🤔

useEffect 없이 외부에서 setMedalItems를 호출하면 무한 루프가 발생!👻

=> setMedalItems를 호출하면 React가 컴포넌트를 다시 렌더링

 

=> 컴포넌트가 다시 렌더링되면 위의 코드가 다시 실행되고 setMedalItems가 호출

=> 이 과정이 계속 반복되면서 무한 루프가 발생☠️ ☠️ ☠️

useEffect(() => {
    const sortedItems = [...medalItems].sort((a, b) => {
      return b[selected] - a[selected];
    });
    setMedalItems(sortedItems);
  }, [selected]); // selected가 변경될때마다 리랜더링!

 

 

2. 로컬 스토리지로 데이터 관리

2-1 로컬 스토리지에 저장

*localStorage에 저장할 key이름 : "medalData"

  1. localStorage.getItem으로 키값이 medalData인 데이터가 있으면 가져오고, 없으면 빈배열로 넣기
  2. JSON.parse로 객체 변환
  3. push로 사용자가 입력한 값을 맨뒤에 추가
  4. localStorage.setItem으로 key이름이 "medalData", value는 위에 데이터를 합친 medalData넣고 
  5. JSON.stringify로 JSON형태로 로컬 스토리지에 저장
const handleSubmit = (e) => {
    ...
    const medalData = JSON.parse(localStorage.getItem("medalData")) || [];
    medalData.push(newItems);
    localStorage.setItem("medalData", JSON.stringify(medalData));
}

 

2-2 로컬 스토리지에서 업데이트

  1. localStorage.getItem으로 키값이 medalData인 데이터를 가져오고
  2. JSON.parse로 객체로 변환
  3. 가져온 데이터를 map으로 돌리면서 사용자가 입력한 나라명과 저장된 나라명을 비교하여 이전 데이터를 spread연산자로 복사한후, 덮어씌우기
  4. 덮어씌운 데이터를 JSON.stringify로 JSON형태로 변환후
  5. localStorage.setItem으로 로컬스토리지에 저장
const updateItem = (item) => {
    return item.country === inputValues.country
      ? {
          ...item,
          ...copyCalculateTotal,
        }
      : item;
  };
  
const handleUpdate = () => {
	...
	let medalData = JSON.parse(localStorage.getItem("medalData"));
    medalData = medalData.map(updateItem);
    localStorage.setItem("medalData", JSON.stringify(medalData));
}

 

2-3 로컬 스토리지에서 삭제

  1. localStorage.getItem으로 key이름이 medalData인 데이터를 가져온후
  2. JSON.parse로 객체로 변환
  3. filter로 사용하여 id를 비교한후, 일치하지 않는 것만 가져와서
  4. JSON.stringify로 json으로 변환후
  5. localStorage.setItem으로 로컬 스토리지에 저장 
const filterById = (items, id) => items.filter((t) => t.id !== id);

const handleDelete = (deleted) => {
	...
    let getData = JSON.parse(localStorage.getItem("medalData"));
    getData = filterById(getData, deleted.id);
    localStorage.setItem("medalData", JSON.stringify(getData));
};

 

2-4 로컬 스토리지에서 가져와서 뿌리기

useEffect로 사용하여 앱이 로드될때 로컬 스토리지에 저장된 데이터를 가져와서 데이터 저장한 useState함수인 setMedalItems에 넣기

useEffect(() => {
    setMedalItems(JSON.parse(localStorage.getItem("medalData")) || []);
}, []);

 

 

🚫번외로 테일윈즈를 사용할때 클래스명이 길어지는 경우가 있는데, 이때 클래스들을 하나의 클래스에 apply해서 만들었다. 근데 이게 좋지 않은 방식임!! 공식 문서에서도 사용하지 말라함! (강민튜터님 감자해여🥔)