[VANILA JS NINJA] MEAL FINDER 구현 - 캐싱, 랜덤버튼, 컴포넌트최적화(4) (21/05/16)

NinjaJuunzzi·2021년 5월 16일
0

[VANILA JS NINJA]

목록 보기
8/9

캐싱

  • App.js
const cache = {};
onSubmit: async (e) => {
      e.preventDefault();

      const {
        target: {
          firstChild: { value: food },
        },
      } = e;
  	// input value값을 food변수에 담아 가져온다.
  
  
  
  	// 해당 food 변수 깂( 음식 명 )으로 데이터를 펫치한적이 있는지를 체크한다. ( 캐싱 되어있는지를 체크한다 )
      if (cache[food]) {
        // 캐시되어있다면
        // 비동기요청 하지않고 캐시스토어에서 데이터를 가져온다.
        this.setState({
          ...this.state,
          meals: cache[food],
          currentFoodKeyword: food,
        });
      } else {
        // 캐시되어있지않다면
        // 비동기 요청을 하여 데이터를 fetch 해온다.
        const newMeals = await fetcher(SEARCH_KEY, food);
        
        // 캐시를 업데이트해준다.
        cache[food] = newMeals.meals;
        this.setState({
          ...this.state,
          meals: newMeals.meals,
          currentFoodKeyword: food,
        });
      }

      e.target.firstChild.value = "";
      //   비동기요청 하고 셋스테이트
    },

Form컴포넌트로 전달되는 onSubmit함수에

입력된 음식명으로 저장된 캐시데이터가 있는지를 점검하는 라인을 추가한다.

있으면 캐시데이터를 가져오고
없으면 비동기요청을 하여 새롭게 fetch해온다. (이 때 온전히 가져왔다면 캐싱한다)

Meals & ResultHeading 컴포넌트 최적화하기

  • 이슈 : 노출된 음식 사진을 누르면 Meals, ResultHeading 컴포넌트가 다시 렌더링 될 필요가 없음에도 렌더링됨.

두 컴포넌트다 제출 시점의 음식명이 달라지는 경우에만 렌더링을 다시하도록 함. ( 제출 시점의 음식명이 같다면 데이터도 같으므로 재 렌더링할 필요없음 )
ex ) useEffect(()=>{},[currentFoodName])

  • shouldComponentUpdate.js
    이전 상태값과 변경된 상태값을 비교하여 변한 경우에만 callback함수를 실행하는 함수이다.
export default function shouldComponentUpdate(
  callback,
  { prevState, nextState }
) {
  if (prevState !== nextState) {
    callback();
  }
}
  • Meals.js
  this.setState = (nextState) => {
    const prevState = this.state;
    this.state = nextState;

    shouldComponentUpdate(
      () => {
        this.render();
      },
      {
        prevState: prevState.currentFoodKeyword,
        nextState: nextState.currentFoodKeyword,
      }
    );
  };
  • ResultHeading.js
   shouldComponentUpdate(
      () => {
        this.render();
      },
      {
        prevState: prevState.currentFoodKeyword,
        nextState: nextState.currentFoodKeyword,
      }
    );
  • SingleMeal.js
  shouldComponentUpdate(
      () => {
        this.render();
      },
      {
        prevState: prevState.singleFood ? prevState.singleFood.idMeal : null,
        nextState: nextState.singleFood.idMeal,
      }
    );

로딩

  • Loading.js
export default function Loading({ $app, initialState }) {
  this.state = initialState;
  this.$target = document.createElement("div");
  this.$target.setAttribute("class", "Loading");
  this.$target.innerHTML = `<i class="fas fa-random"></i>`;
  // this.$target.textContent = "loading!!";
  $app.appendChild(this.$target);

  this.setState = (nextState) => {
    this.state = nextState;
    this.render();
  };
  this.render = () => {
    // 상태값으로 로딩중이면 보여주게하고, 로딩중이 아니면 안보이게한다.
    this.$target.style.display = this.state.isLoading ? "block" : "none";
  };
  this.render();
}
  • App.js
  const loading = new Loading({
    $app,
    initialState: { isLoading: this.state.isLoading },
  });
  • onSubmit Method
// 비동기 요청 전 로딩을 걸어준다.
 this.setState({ ...this.state, isLoading: true });
// 비동기 요청 수행
        const newMeals = await fetcher(SEARCH_KEY, food);

        cache[food] = newMeals.meals;
// 비동기 요청 끝났으므로 로딩 풀어준다.
        this.setState({
          ...this.state,
          meals: newMeals.meals,
          currentFoodKeyword: food,
          singleFood: null,
          isLoading: false,
        });

제출을 하면 로딩컴포넌트가 잠시 노출되고 없어진 후 비동기 요청한 데이터가 노출된다.

Random Single Meal

다음은 랜덤 버튼을 누르면 수행되는 핸들러함수이다. (RandomButton컴포넌트로 보내준다)

  • App.js
 onRandomButtonHandler: async (e) => {
   // 비동기 요청전 로딩을 걸어준다.
   this.setState({ ...this.state, isLoading: true });
   // 비동기 요청을 하여 랜덤 밀을 받는다.
      const {
        meals: [randomMeal],
      } = await fetcher(RANDOM_SINGLE_KEY);
   
   // 로딩을 풀어주고 기존의 meals, currentFoodKeyword 를 초기화, 방금 받은 데이터를 상탯값으로 전달한다.
      this.setState({
        ...this.state,
        isLoading: false,
        singleFood: randomMeal,
        meals: null,
        currentFoodKeyword: null,
      });
    },

랜덤음식과 검색음식

  • 검색음식 데이터가 노출되면 랜덤 음식 데이터는 초기화되어야하고

  • 랜덤음식 데이터가 노출되면 검색 음식 데이터가 초기화되어야함.

다음은 FormRandomButton에 붙일 핸들러 함수 일부이다.

const newMeals = await fetcher(SEARCH_KEY, food);

        cache[food] = newMeals.meals;
        this.setState({
          ...this.state,
          // 방금 얻은 데이터를 state에 넣어준다.
          meals: newMeals.meals,
          // 제출 시 입력되어있는 키워드 값을 넣어준다.
          currentFoodKeyword: food,
          
          // single Food 값은 지워준다
          singleFood: null,
          
          // 데이터가 왔으므로 로딩을 지워준다.
          isLoading: false,
        });
const {
        meals: [randomMeal],
      } = await fetcher(RANDOM_SINGLE_KEY);
      this.setState({
        ...this.state,
        // 로딩을 지워준다.
        isLoading: false,
        
        // 방금 얻은 randomMeal을 singleFood 속성으로 
        singleFood: randomMeal,
        
        // 기존의 meals 상태를 비워준다.
        meals: null,
        
        // meals를 지웟으므로 키워드 값도 지워준다.
        currentFoodKeyword: null,
      });
profile
Frontend Ninja

0개의 댓글