본문 바로가기
  • 삽질하는 자의 블로그
메인-프로젝트/Next.js - 오늘 뭐먹지? 프로젝트

8. 칼로리 계산기를 만들어보자. with DOM

by 이게뭐당가 2022. 12. 3.

2022.12.12 추가사항

"useReducer" "func.bind()" 를 사용하면 "REAL DOM" 에 "접근할 필요가 없겠구나"를 깨달음

 

1. [선택된 항목들]

useReducer 의 "ADD type"의 함수를 만들어 Array.concat() 새 배열을 만들어 추가되게만든다. (추가된 항목들의 배열)

 

2. [추가된 항목마다 달린 버튼들]

각 항목들은 Array.map() 을 사용하여 열거될것이다. 열거된 항목들마다, 각각의 버튼이 달릴 것이다.

 

3. [버튼들 각각의 함수]

각 버튼들은, function.bind() 의 부분적용을 사용하여, 각 id 파라미터를 고정시키고,  그에 맞는 배열의 amount 수정하는 함수를 만든다.

 

[종합]

concat 으로 추가된 배열은, 각 항목마다 버튼이 달리고, 그 버튼은 id 가 고정되어, 각 항목마다의 amount 만 바꾸는 로직을 가짐.

4. [ 최종 가격 ]

총 값은, Array.reduce() 를 사용한다. concat으로 추가된 배열(원 배열 말고) 의 각 amount 와 각 price를 곱해 최종 값이 나온다. 

 

앞으로는 이렇게 하자

 

   =============================================================================================='

 

<기존 방식> - 따라하지 마시오

 

 

React 는 Virtual DOM 을 이용하여, 필요한 부분만 변경하여 적용하는 방법을 사용합니다.

그래서 document.getElementById() 나,  document.querySelector() 를 사용하지 않는 것을 권장합니다.

 

직접 DOM 에 접근하는 것은, REACT 의 생명주기와는 다른 시간에 적용이 될 뿐더러, 통제가 잘 되지 않기 때문입니다.

대신 useState 를 사용해 (혹은 useRef 지만 권장하지 않음), 상태를 통제합니다.

 

다만 현재, 무한으로 증가하는 값에 대해, 각각의 State 수를 통제하는 방법을 아직 알지 못해, 

실제 DOM 에, querySelectorAll() 을 사용하여, 모든 값에 접근해, 더하는 방식을 사용하였습니다.

 

후에, "상태관리" 라이브러리 라던가, 상태관리에 대하여 더 배워야겠다

<계산기 로직>

   1. 수량 변경
   2. 수량을 기준, 다른 DOM 을 자동 변경
   3. 변경된 DOM 전부를 지정(querySelectorAll)
   4. 지정된 모든 DOM을 for 문을 사용하여, Array 형태로 만듬
   5. Array 를 reduce 를 사용해 전부 합산

<셀렉터 로직>

1. 카테고리를 누른다.

2. 카테고리에 알맞은 음식들이 나온다. ( Arrayt.filter() 를 사용한, 검색조건 활용)

3. 음식을 누르면, DB에서 칼로리를 가져온다.

4. 계산기 로직에 접근한다.

 

 

짜잔

코드를 봐보자

 

 import styles from "./caculate-selector.module.css";
import { useState, useEffect } from "react";
import Button from "../ui/card/button";

function CaloireFoodSeletor(props) {
  const { foodData } = props;
  const [filteredData, setFilterdData] = useState([]);                   // 카테고리 선택하면, filter 되는 Data 정리
  const [foodValue, setFoodValue] = useState();                        // 음식을 눌렀을때, 그 음식에 대한 foodid 선택
  const [foodInCalculator, setFoodInCalculator] = useState([]);  // 총 누른 음식들의 Data를 Array 형태로 정리

  const [totalCalories, setTotalCalorie] = useState(0);

  const sortedFoodData = filteredData.sort((A, B) =>                  // 전체 Data를 순서대로 정렬
    A.name > B.name ? 1 : -1
  );

  useEffect(() => {                                                        // foodValue 가 바뀔때, [기존 + 새 foodid로 가져온 foodData] 를 삽입
    if (foodValue) {
      setFoodInCalculator((prev) => [
        ...prev,
        foodData.find((food) => food.id === String(foodValue)),
      ]);
    }
  }, [foodValue]);


  function resetHandler() {                                  // [reset 로직]
    setFoodInCalculator([]);
    setTotalCalorie(0);
  }


  function filterFoodWithCategory(category) {                         // [필터링 로직] 카테고리 눌렀을때,
    const filterd = foodData.filter((food) => food.category === category);
    setFilterdData(filterd);
  }

  function calculate(e) {                                                                                // [계산기 로직]  계산을 위해 DOM 에 접근                                               
    let foodCalorie = e.target.parentElement.children[2].children[0].value;
    let quantity = e.target.parentElement.children[1].value;

    let total = foodCalorie * quantity;

    e.target.parentElement.children[3].children[0].value = Math.ceil(total);

    const alltotals = document.querySelectorAll(".total");

    let newArray = [];
    for (const alltotal of alltotals) {
      newArray.push(alltotal.value);
    }
    let totalCalories = newArray.reduce((sum, current) => +sum + +current);

    setTotalCalorie(Math.ceil(totalCalories));
  }

  return (
    <main className={styles.maindiv}>
      <section className={styles.selector}>
        <div>
          <h2> 선택기 </h2>
          <hr></hr>
          <h4> 카테고리</h4>                                                                                     // 1  카테고리 선택
          <hr></hr>
          <ul className={styles.category}>
            <li onClick={() => filterFoodWithCategory("다이어트")}>다이어트</li>
            <li onClick={() => filterFoodWithCategory("한식")}>한식</li>
            <li onClick={() => filterFoodWithCategory("양식")}>양식</li>
            <li onClick={() => filterFoodWithCategory("일식")}>일식</li>
            <li onClick={() => filterFoodWithCategory("베트남")}>베트남</li>
            <li onClick={() => filterFoodWithCategory("중식")}>중식</li>
            <li onClick={() => filterFoodWithCategory("디저트")}>디저트</li>
          </ul>
          <hr></hr>
        </div>
        <div>
          <h4> 음식 </h4>
          <hr></hr>

          {filteredData && (                              //2.  카테고리를 선택해, filteredData 가 생겼다면, 그 하위 음식들 선택
            <ul className={styles.subSelector}>
              {sortedFoodData.map((food) => (
                <li
                  key={food.id}
                  value={food.id}
                  onClick={(e) => setFoodValue(e.target.value)}
                >
                  {food.name}
                </li>
              ))}
            </ul>
          )}
        </div>
      </section>

      <section className={styles.calculator}>
        <h2> 계산기</h2>
        <hr></hr>
        <div>
          <ul className={styles.calculatorHeader}>
            <li> 선택음식</li>
            <li>
              섭취량<span className={styles.unit}>x100g</span>{" "}
            </li>
            <li className={styles.caloriepergram}> 칼로리/100g</li>
            <li> 총 칼로리</li>
          </ul>
          <hr></hr>

          {foodInCalculator.map((food) => (                                         //3.  선택해놓은, 음식들의 데이터를 계산기에 생성
            <ul key={food.id} className={styles.calculatorFoodList}>
              <li>{food.name}</li>
              <input
                type={"number"}
                step={1}
                defaultValue={0}
                onChange={(e) => calculate(e)}
                className={styles.quantity}
              />
              <li className={styles.caloriepergram}>
                <input type={"number"} value={food.calorie} readOnly />
              </li>
              <li>
                <input
                  type={"number"}
                  readOnly
                  className={"total"}
                  defaultValue={0}
                />
              </li>
            </ul>
          ))}
          <hr />
          <h4>총 칼로리</h4>
          <div className={styles.totalCalories}>
            <input type={"number"} value={totalCalories} readOnly />
            <span> Kcal</span>
          </div>
        </div>
        <div className={styles.buttondiv}>
          <Button onClick={resetHandler}> 초기화 </Button>
        </div>
      </section>
    </main>
  );
}

export default CaloireFoodSeletor;

댓글