언어 공부/React

[React] API연동 - useReducer 사용

수기 2022. 4. 13. 18:27

useAsync.js

import { useReducer, useEffect } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'LOADING':
      return {
        loading: true,
        data: null,
        error: null
      };
    case 'SUCCESS':
      return {
        loading: false,
        data: action.data,
        error: null
      };
    case 'ERROR':
      return {
        loading: false,
        data: null,
        error: action.error
      };
    default:
      throw new Error(`Unhandled action type: ${action.type}`);
  }
}

function useAsync(callback, deps = [], skip = false) {
    console.log(skip)
  const [state, dispatch] = useReducer(reducer, {
    loading: false,
    data: null,
    error: false
  });

  const fetchData = async () => {
    dispatch({ type: 'LOADING' });
    try {
      const data = await callback();
      dispatch({ type: 'SUCCESS', data });
    } catch (e) {
      dispatch({ type: 'ERROR', error: e });
    }
  };

  useEffect(() => {
        console.log(skip)
      if(skip) return;
    fetchData();
    // eslint 설정을 다음 줄에서만 비활성화
    // eslint-disable-next-line
  }, deps);

  return [state, fetchData];
}

export default useAsync;

 

 

Users.js

import React, { useState, useEffect,useReducer } from 'react';
import axios from 'axios';
import useAsync from './useAsync';

async function getUsers(){
  const response = await axios.get(
    'https://jsonplaceholder.typicode.com/users'
  );
  return response.data
}

function Users() {
  const [state, refetch] = useAsync(getUsers, [], true); 

  const {loading, data: users, error } = state;

  if (loading) return <div>로딩중..</div>;
  if (error) return <div>에러가 발생했습니다</div>;
  if (!users) return <button onClick={refetch}>불러오기</button>;

  return (
    <>
    <ul>
      {users.map(user => (
        <li key={user.id}>
          {user.username} ({user.name})
        </li>
      ))}
    </ul>
    <button onClick={refetch}>다시 불러오기</button>
    </>
  );
}

export default Users;

useAsync에서 3번째 파라미터로 skip을 추가했다. 기본값은 false이다.

Users에서 useAsync를 받아올 때, skip의 값을 true로 받았다. useAsync는 skip값이 true일 때 fetchData()함수를 실행하지 않으므로, Users컴포넌트에서는 if(!users) 에 걸려서 return <button onClick={refetch}>불러오기</button>; 을 리턴한다.

 

** skip값이 true면 refetch를 해도 계속 if(!users)에 걸려야 하지 않나? 왜 불러오기 버튼을 누르면 false인것처럼 동작을 하는지?

console.log(skip)을 찍어봐도 skip값은 계속 true인데..?  useAsync()의 초기값을 true로 설정해도 동일한 결과가 나타난다. 

 

👉  해결

useAsync.js에서 마지막에 return[state, fetchData] 가 있기때문에 skip여부와 상관없이 렌더링이 되는거였다..

useEffect는 초기 렌더링할 때 딱 한번 실행되서 불러오기 버튼만 나왔던 거였고, 이후부터는 skip여부에 상관없이 fetchData를 실행한다.

 

 

 

 

API 에 파라미터가 필요한 경우

useAsync에 skip = true 로 할 경우, useEffect에서 deps가 바뀔 때 마다 업데이트 되므로, id값의 변화에 따라 useEffect가 실행되어야 하는데 skip때문에 실행이 되지 않음. -> skip = true로 지정 시 id파라미터로 api를 불러올 수 없음

 

 

 

04. react-async 로 요청 상태 관리하기

로딩중 문제 해결하기

 

 

참고한 블로그
https://react.vlpt.us/integrate-api/03-useAsync.html