[React] HTTP: 로딩 및 데이터 state 처리하기

summereuna🐥·2023년 5월 20일
0

React JS

목록 보기
54/69

로딩 및 데이터 state 처리하기

앱을 새로고침하고 fetch 버튼을 클릭하면 영화가 화면에 표시되기까지 시간이 조금 걸린다.
이럴 때, 실제로는 로딩 아이콘이나 로딩 텍스트를 표시하여 사용자에게 현재 데이터를 불러오고 있다는 신호를 보내기도 한다.

로딩은 상태 관리를 통해 할 수 있다.

영화 데이터의 상태를 가져오면, 현재 이 데이터가 존재하는지 알 수 있다.
하지만 기다리는 중인지 알려주려면 또 다른 상태를 만들어야 한다.

isLoading상태를 만들어서 로딩 상태를 관리해보자.

import React, { useState } from "react";

import MoviesList from "./components/MoviesList";
import "./App.css";

function App() {
  const [movies, setMovies] = useState([]);
  // ✅ 로딩중인지 상태 관리
  const [isLoading, setIsLoading] = useState(false);

  const fetchMoviesHandler = async () => {
    // ✅ 페치 버튼 클릭하면 loading 상태 true
    setIsLoading(true);

    const response = await fetch("https://swapi.dev/api/films/");
    const data = await response.json();
    const transformedMovies = data.results.map((movieData) => {
      return {
        id: movieData.episode_id,
        title: movieData.title,
        openingText: movieData.opening_crawl,
        releaseDate: movieData.release_date,
      };
    });
    setMovies(transformedMovies);
    
    // ✅ 비동기이긴 하지만 movies 데이터 업데이트 후
    // loading 상태 다시 false로 만들기
    setIsLoading(false);
  };

  return (
    <React.Fragment>
      <section>
        <button onClick={fetchMoviesHandler}>Fetch Movies</button>
      </section>
      <section>
        //🔥 로딩중이 아닐 때(즉, 로딩 완료 시) && 무비 배열에 데이터 있을 때 && 데이터 화면에 표시하기 
        {!isLoading && movies.length > 0 && <MoviesList movies={movies} />}
        //🔥 로딩은 완료되었지만 && 무비 배열에 데이터 없으면 && 영화가 없다고 표시하기
        {!isLoading && movies.length === 0 && <p>Found no movies.</p>}
        //🔥 로딩 중일 때 && 로딩중이라고 표시하기
        {isLoading && <p>Loading...</p>}
      </section>
    </React.Fragment>
  );
}

export default App;
import useHttp from "../../hooks/use-http";
import Section from "../UI/Section";
import TaskForm from "./TaskForm";

const NewTask = (props) => {
  const { isLoading, error, sendRequest: sendTaskRequest } = useHttp();
  //깊은 중첩 구조 피하기 위해 taskText 매개변수로 받기..^^...뭔소리여 아...

  //두 번째 인자는 응답 데이터 받아서 뭔가 하는 함수
  //taskData가 커스텀훅의 data
  const createTask = (taskText, taskData) => {
    const generatedId = taskData.name; // firebase-specific => "name" contains generated id
    const createdTask = { id: generatedId, text: taskText };

    props.onAddTask(createdTask);
  };

  const enterTaskHandler = async (taskText) => {
    //폼이 제출될 때 마다 enterTaskHandler가 트리거되는데, 그럴때 마다 http 요청이 호출되어야 하므로 여기에서 호출한다.
    sendTaskRequest(
      {
        url: "https://react-http-35c4a-default-rtdb.firebaseio.com/tasks.json",
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: { text: taskText },
      },
      createTask.bind(null, taskText)
      //bind()메소드 사용하여 taskText 매개변수 전달하기..
      //bind()는 JS 기본 메서드로 어떤 함수에 대해서도 이를 사전 구성하기 위해 사용할 수 있다.
      //bind에 보내는 첫 번재 인자는 실행이 예정된 함수에서 this 예약어를 사용하게 하는데, 여기서는 쓸모가 없으므로 null로 둔다.
      //두 번째 인자를 호출 예정인 함수가 받는 첫번째 인자가 되므로 taskText를 전달하여 제출된 폼에서 taskTest를 찾게 하면 된다.

      //나머지 인자인 taskData는 사전 설정되었기 때문에 이 위치에서 받는다.
      //함수가 실제로 호출되는 useHttp에서 전달되는 다른 인자인 applyData의 경우, 간단하게 이 매개변수의 목록 끝에 추가하여 처리하면 된다.
      //여기에 bind를 호출했기 때문에 이렇게 커스텀훅의 applyData의 유일한 인자로 전달되는 data는 createTask의 두 번째 인자로 추가된다.
    );
  };

  return (
    <Section>
      <TaskForm onEnterTask={enterTaskHandler} loading={isLoading} />
      {error && <p>{error}</p>}
    </Section>
  );
};

export default NewTask;

//이펙트 함수가 아닌 enterTaskHandler 에서만 sendTaskRequest를 호출하고 있기 때문에 useCallback 호출할 필요 없음
//따라서 무한루프 같은 문제는 발생하지 않음
//여기서 POST 요청은 컴포넌트가 재평가되어도 전송되지 않는다. 폼이 제출될 때맏 함수가 실행된다.
//App 컴포넌트와 이 컴포넌트의 차이 ㅇㅇ~

//App 컴포넌트에서는 이펙트 함수 안에서 요청을 보내서 요청 전송이 자주 발생할 위험성이 있었음
profile
Always have hope🍀 & constant passion🔥

0개의 댓글