상세 컨텐츠

본문 제목

[React] Fetch data in React (1)

😎 지식/FE-리액트🌐

by :Eundms 2021. 5. 27. 10:56

본문

이번 이슈 해결을 위해서, 영어 블로그를 참고하였고, 이를 한국어로 정리한 글입니다.

1단계. Fetch data in React (use native fetch API)

https://www.robinwieruch.de/react-fetching-data

리액트 컴포넌트 Tree 중 한 곳에서 Third Party API를 사용한다고 가정하자.


 

Q1. 리액트 어느 컴포넌트에서 Fetch를 진행해야 할까?

고려해야 할 사항

1. 어떤 컴포넌트에서 이 Data가 필요한가

2. 어디에서 로딩 중이라는 표시를 보여주고 싶은가

3. 에러 메시지를 어디에서 보여주고 싶은가?

 

Q2. React LifeCycle Method에서 찾아보자

- componentDidMount()

render 함수를 최초로 실행한 후, 받아온 Data가 setState 함수로 변경이 되면, 다시 render 된다.

 

Fetch함수는 javascript의 promise를 사용하여 비동기 응답을 해결한다. (더 조사 필요)

 

import React, { Component } from 'react';
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';
 
class App extends Component {
  constructor(props) {
    super(props);
 
    this.state = {
      hits: [],
    };
  }
 
  componentDidMount() {
    fetch(API + DEFAULT_QUERY)
      .then(response => response.json())
      .then(data => this.setState({ hits: data.hits }));
  }
 
  render() {
    const { hits } = this.state;
    return (
      <ul>
        {hits.map(hit =>
          <li key={hit.objectID}>
            <a href={hit.url}>{hit.title}</a>
          </li>
        )}
      </ul>
    );
  }
}
 }
 
export default App;

componentDidMount() 전에 이미 render()가 실행되었음에도 불구하고,

hits props가 empty array로 이미 정의되었기 때문에 null pointer 에러가 나지 않는다.

Q3. Loading Spinner and Error Handling 

class App extends Component {
  constructor(props) {
    super(props);
 
    this.state = {
      hits: [],
      isLoading: false,
      error : null,
    };
  }
 
  componentDidMount() {
    this.setState({ isLoading: true });
 
    fetch(API + DEFAULT_QUERY)
      .then(response => {
        if (response.ok) {
          return response.json();
        } else {
          throw new Error('Something went wrong ...');
        }
      })
      .then(data => this.setState({ hits: data.hits, isLoading: false }))
      .catch(error => this.setState({ error, isLoading: false }));
  }
 
   render() {
    const { hits, isLoading, error } = this.state;
 
    if (error) {
      return <p>{error.message}</p>;
    }
 
    if (isLoading) {
      return <p>Loading ...</p>;
    }
 
    return (
      <ul>
        {hits.map(hit =>
          <li key={hit.objectID}>
            <a href={hit.url}>{hit.title}</a>
          </li>
        )}
      </ul>
    );
  }
}
 
export default App;

- Loading

isLoading State는 asynchronous request가 일어나고 있다는 것을 알려주기 위해 사용된다.

fetch 진행 바로 직전에 isLoading을 true로 바꾸고, 

fetch가 다 일어난 후에 setState를 활용해서 isLoading을 false로 바꾼다. 

그리고 render함수 내에서 isLoading이 true인 경우 Loading이라는 글자가 보이도록 한다.

(이거 너무 좋은 거 같아 - https://github.com/danilowoz/react-content-loader)

- Error

catch로 error를 잡으면 error를 state에 저장해주고, isLoading false로 바꾼다.

=> native fetch API는 404에러가 뜨면, catch 블록까지 가지 않음.

이를 해결하기 위해 response를 분석하여 ok가 아니라면 throw new Error('something went wrong')으로 catch 블록에 도달할 수 있도록 한다.
에러를 보여주고 싶다면, if (error) 이부분으로 error메시지를 보여준다.


2단계. AXIOS 사용해서 React에서 Fetch하기

error가 발생했을 때, axios는 자동으로 catch에 가기 때문에 error를 throw할 필요가 없다.

axios는 자동으로 json response를 준다.

import React, { Component } from 'react';
import axios from 'axios';
 
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';
 
class App extends Component {
  constructor(props) {
    super(props);
 
    this.state = {
      hits: [],
      isLoading: false,
      error: null,
    };
  }
 
  componentDidMount() {
    this.setState({ isLoading: true });
 
    axios.get(API + DEFAULT_QUERY)
      .then(result => this.setState({
        hits: result.data.hits,
        isLoading: false
      }))
      .catch(error => this.setState({
        error,
        isLoading: false
      }));
  }
 
  ...
}
 
export default App;

Q1. 버튼을 눌렀을 때, data를 받아오고 싶어요

이전까지는, React의 componentDidMount lifecycle method에서 api 를 이용해 data를 받았지만,

버튼을 눌렀을 때, data를 받아오도록 할 수 도 있다.

이때는 componentDidMount와 같이 lifecycle을 사용하지 않고, 사용자가 직접 함수를 정의해서 사용한다.

import React, { Component } from 'react';
import axios from 'axios';
 
const API = 'https://hn.algolia.com/api/v1/search?query=';
const DEFAULT_QUERY = 'redux';
 
class App extends Component {
  constructor(props) {
    super(props);
 
    this.state = {
      hits: [],
      isLoading: false,
      error: null,
    };
  }
 
  getStories() {
    this.setState({ isLoading: true });
 
    axios.get(API + DEFAULT_QUERY)
      .then(result => this.setState({
        hits: result.data.hits,
        isLoading: false
      }))
      .catch(error => this.setState({
        error,
        isLoading: false
      }));
  }
 
  ...
}
 
export default App;

react는 get method 밖에 없지만, axios를 사용한다면 get과 post를 사용할 수 있다.


3단계. Test Data Fetching in React

> [extensive React testing tutorial] https://www.robinwieruch.de/react-testing-tutorial

create-react-app으로 aplication을 setting 했다면, Jest가 test runner, assertion library로 정해져 있다.

아니라면, Mocha나 Chai를 대신 사용할 수도 있다.

리액트 컴포넌트를 testing할 때, 이 분은 Enzyme를 종종 사용하곤 한다고 한다.

--- test 하는 부분은 차후에 읽도록 하자.


 

 

 

 

 

 

관련글 더보기

댓글 영역