5 React 데이터 가져오기 패턴

Sooner or later you will need to fetch data from an API, let's have a look at how we can handle data in React!👌



이 튜토리얼에서는 React에서 가장 일반적인 데이터 가져오기 패턴을 다룰 것입니다.

Originally posted on Nordschool.



당신은 읽습니까? 해보자! 💪

개요



먼저 큰 그림을 본 다음 더 깊이 파고들어 봅시다.

우리가 다룰 패턴:
  • Standalone
  • HOC
  • With Hooks
  • Render Props
  • Redux Custom Middleware

  • 프로젝트 구조



    다양한 데이터 가져오기 패턴을 보여주기 위해 작은 반응 프로젝트를 만들었습니다. 초기화된 프로젝트create-react-app에는 표준 구조가 있습니다. 👇

    ├── README.md
    ├── package.json
    ├── public
    │   ├── favicon.ico
    │   ├── index.html
    │   ├── logo192.png
    │   ├── logo512.png
    │   ├── manifest.json
    │   └── robots.txt
    ├── src
    │   ├── App.css
    │   ├── App.js
    │   ├── App.test.js
    │   ├── actions
    │   │   ├── api.js
    │   │   ├── index.js
    │   │   └── types.js
    │   ├── hooks
    │   │   ├── UseDataApi.js
    │   ├── components
    │   │   ├── HOC.js
    │   │   ├── Standalone.js
    │   │   ├── PostsList.js
    │   │   ├── RenderProps.js
    │   │   ├── WithCustomMiddleware.js
    │   │   ├── WithCustomHook.js
    │   │   └── WithHooks.js
    │   ├── index.css
    │   ├── index.js
    │   ├── middleware
    │   │   └── api.js
    │   ├── reducers
    │   │   └── index.js
    │   ├── serviceWorker.js
    │   └── store
    │       └── index.js
    └── yarn.lock
    

    우리는 구성 요소를 다룰 것입니다.

    기본 루트 구성 요소는 다음과 같습니다.

    // App.js
    import React from 'react';
    import './App.css';
    
    import Standalone from './components/Standalone';
    import HOC from './components/HOC';
    import WithHooks from './components/WithHooks';
    import WithCustomHook from './components/WithCustomHook';
    import RenderProps from './components/RenderProps';
    import WithCustomMiddleware from './components/WithCustomMiddleware';
    import PostsList from './components/PostsList';
    
    function App() {
      return (
        <div className="App">
          <h3>Standalone</h3>
          <Standalone />
          <h3>HOC</h3>
          <HOC />
          <h3>WithHooks</h3>
          <WithHooks />
          <h3>With Custom Hook</h3>
          <WithCustomHook />
          <h3>Render Props</h3>
          <RenderProps children={PostsList} />
          <h3>With Custom Middleware</h3>
          <WithCustomMiddleware />
        </div>
      );
    }
    
    export default App;
    

    가장 간결한 패턴부터 시작하겠습니다...

    독립형



    이 독립 실행형 구성 요소는 데이터 가져오기 및 렌더링을 모두 처리합니다.

    // components/Standalone.js
    import React, { Component } from 'react';
    import axios from 'axios';
    
    import Table from '@material-ui/core/Table';
    import TableBody from '@material-ui/core/TableBody';
    import TableCell from '@material-ui/core/TableCell';
    import TableHead from '@material-ui/core/TableHead';
    import TableRow from '@material-ui/core/TableRow';
    import Paper from '@material-ui/core/Paper';
    
    // API END POINT
    const POSTS_SERVICE_URL = 'https://jsonplaceholder.typicode.com/posts';
    
    class Standalone extends Component {
      state = {
        // Initial state.
        isFetching: false,
        posts: []
      };
    
      render() {
        return (
          <Paper>
            <Table aria-label="simple table">
              <TableHead>
                <TableRow>
                  <TableCell>Id </TableCell>
                  <TableCell>Title</TableCell>
                  <TableCell>Body</TableCell>
                </TableRow>
              </TableHead>
              <TableBody>
                {this.state.posts.map(row => (
                  <TableRow key={row.id}>
                    <TableCell component="th" scope="row">
                      {row.id}
                    </TableCell>
                    <TableCell>{row.title}</TableCell>
                    <TableCell>{row.body}</TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
            <p>{this.state.isFetching ? 'Fetching posts...' : ''}</p>
          </Paper>
        );
      }
    
      componentDidMount() {
        this.fetchPosts();
      }
    
      async fetchPostsAsync() {
        try {
          this.setState({ ...this.state, isFetching: true }); // Sets loading state.
          const response = await axios.get(POSTS_SERVICE_URL);
          this.setState({
            ...this.state,
            isFetching: false,
            posts: response.data.slice(0, 5) // Take first 5 posts only
          });
        } catch (e) {
          console.log(e);
          this.setState({ ...this.state, isFetching: false });
        }
      }
    
      fetchPosts = this.fetchPostsAsync;
    }
    
    export default Standalone;
    

    데이터 가져오기에서 뷰를 분리할 수 있는지 봅시다 🤓...

    HOC



    HOC(Higher-Order Component) 패턴은 React에서 일반적입니다. 이와 같은 구성 요소는 컨테이너 구성 요소라고도 합니다.

    아이디어는 간단합니다. 데이터 가져오기는 데이터 표시와 분리됩니다.

    // HOC.js
    import React, { Component } from 'react';
    import Simple from './Simple';
    
    import axios from 'axios';
    
    const POSTS_SERVICE_URL = 'https://jsonplaceholder.typicode.com/posts';
    
    class HOC extends Component {
      state = {
        isFetching: false,
        posts: []
      };
    
      render = () => (
        <Simple data={this.state.posts} isFetching={this.state.isFetching}></Simple>
      );
    
      componentDidMount() {
        this.fetchPosts();
      }
    
      async fetchPostsAsync() {
        try {
          this.setState({ ...this.state, isFetching: true });
          const response = await axios.get(POSTS_SERVICE_URL);
          this.setState({
            ...this.state,
            isFetching: false,
            posts: response.data.slice(0, 5)
          }); // Take first 5 posts only
        } catch (e) {
          console.log(e);
          this.setState({ ...this.state, isFetching: false });
        }
      }
    
      fetchPosts = this.fetchPostsAsync;
    }
    
    export default HOC;
    

    프레젠테이션 구성 요소는 다음과 같습니다.

    // PostsList.js
    import React from 'react';
    import Table from '@material-ui/core/Table';
    import TableBody from '@material-ui/core/TableBody';
    import TableCell from '@material-ui/core/TableCell';
    import TableHead from '@material-ui/core/TableHead';
    import TableRow from '@material-ui/core/TableRow';
    import Paper from '@material-ui/core/Paper';
    
    const PostsList = props => {
      return (
        <Paper>
          <Table aria-label="simple table">
            <TableHead>
              <TableRow>
                <TableCell>Id </TableCell>
                <TableCell>Title</TableCell>
                <TableCell>Body</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {props.data.map(row => (
                <TableRow key={row.id}>
                  <TableCell component="th" scope="row">
                    {row.id}
                  </TableCell>
                  <TableCell>{row.title}</TableCell>
                  <TableCell>{row.body}</TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
          <p>{props.isFetching ? 'Fetching posts...' : ''}</p>
        </Paper>
      );
    };
    
    export default PostsList;
    

    하지만 일을 처리하는 더 나은 방법이 있을 수 있다고 말하면 어떻게 될까요? 😁

    이것이 Hooks에서 어떻게 작동하는지 살펴보겠습니다.

    후크 포함



    이 패턴은 HOC와 유사하지만 후크와 함께 기능적 구성 요소를 사용합니다.

    // WithHooks.js
    import React, { useEffect, useState } from 'react';
    import axios from 'axios';
    import Simple from './Simple';
    
    const POSTS_SERVICE_URL = 'https://jsonplaceholder.typicode.com/posts';
    
    function WithHooks() {
      const [data, setData] = useState({ posts: [], isFetching: false });
    
      useEffect(() => {
        const fetchUsers = async () => {
          try {
            setData({ ...data, isFetching: true });
            const response = await axios.get(POSTS_SERVICE_URL);
            setData({
              ...data,
              posts: response.data.slice(0, 5),
              isFetching: false
            });
          } catch (e) {
            console.log(e);
            setData({ ...data, isFetching: false });
          }
        };
        fetchUsers();
      }, []); // Runs once
    
      return <Simple data={data.posts} isFetching={data.isFetching} />;
    }
    
    export default WithHooks;
    

    한 단계 더 나아가 모든 API에서 데이터를 가져오는 일반 후크를 만들 수도 있습니다.

    일반 데이터 가져오기 후크의 간단한 버전:

    // hooks/UseDataApi.js
    import { useEffect, useState } from 'react';
    import axios from 'axios';
    
    const useDataApi = url => {
      // This is just for demo purposes, you probably want to separate the data from loading state and potentially add other states such as failures, etc..
      const [dataState, setDataState] = useState({ data: [], isFetching: false }); 
      const [endpointUrl] = useState(url);
    
      useEffect(() => {
        const fetchDataFromApi = async () => {
          try {
            setDataState({ ...dataState, isFetching: true });
            const response = await axios.get(endpointUrl);
            setDataState({
              ...dataState,
              data: response.data,
              isFetching: false
            });
          } catch (e) {
            console.log(e);
            setDataState({ ...dataState, isFetching: false });
          }
        };
        fetchDataFromApi();
      }, []); // Runs once
    
      return [dataState];
    };
    
    export default useDataApi;
    

    후크가 준비되면 다음과 같이 사용할 수 있습니다 ...

    // components/WithCustomHook.js
    import React from 'react';
    import UseDataApi from '../hooks/UseDataApi';
    import PostsList from './PostsList';
    
    const POSTS_SERVICE_URL = 'https://jsonplaceholder.typicode.com/posts';
    
    function WithHooks() {
      const [dataState] = UseDataApi(POSTS_SERVICE_URL);
    
      return (
        <PostsList
          data={dataState.data.slice(0, 5)}
          isFetching={dataState.isFetching}
        />
      );
    }
    
    export default WithHooks;
    

    We recommend this pattern for most cases! This pattern is reusable and does separation of concern well.



    좋아, 지금까지 너무 좋아.

    그러나 동일한 데이터를 표시하는 많은 프레젠테이션 구성 요소가 있는 경우에는 어떻게 해야 합니까?

    일반적으로 Hooks는 대부분의 논리 캡슐화 사례를 다룰 수 있습니다. 그러나 몇 가지limitations가 있을 수 있습니다.

    렌더링 소품



    이 사용 사례에 대해 렌더링 소품 대신 후크를 사용할 수 있지만 렌더링 소품은 또 다른 실행 가능한 옵션입니다.

    렌더 소품은 다양한 프레젠테이션 구성 요소에 대해 재사용 가능한 래퍼 역할을 합니다.

    // RenderProps.js
    import { Component } from 'react';
    import axios from 'axios';
    
    const POSTS_SERVICE_URL = 'https://jsonplaceholder.typicode.com/posts';
    
    class RenderProps extends Component {
      state = {
        isFetching: false,
        data: []
      };
    
      render = () => this.props.children(this.state);
    
      componentDidMount() {
        this.fetchPosts();
      }
    
      async fetchPostsAsync() {
        try {
          this.setState({ ...this.state, isFetching: true });
          const response = await axios.get(POSTS_SERVICE_URL);
          this.setState({
            ...this.state,
            isFetching: false,
            data: response.data.slice(0, 5)
          }); // Take first 5 posts only
        } catch (e) {
          console.log(e);
          this.setState({ ...this.state, isFetching: false });
        }
      }
    
      fetchPosts = this.fetchPostsAsync;
    }
    
    export default RenderProps;
    

    이것이 Vanilla React 패턴의 전부입니다! 🙌

    Redux는 어떻습니까? 어떻게 생겼을까요? 슬쩍 보여드리겠습니다.

    Redux 커스텀 미들웨어



    다음은 custom middleware 을 사용하는 간단한 예입니다.

    // components/WithCustomMiddleware.js
    
    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import PostsList from './PostsList';
    import { fetchPosts } from '../actions';
    
    class WithCustomMiddleware extends Component {
      state = {};
    
      componentDidMount() {
        this.props.fetchPosts();
      }
    
      render = () => (
        <PostsList
          data={this.props.data}
          isFetching={this.props.isFetching}
        ></PostsList>
      );
    }
    
    const mapStateToProps = ({ data = [], isFetching = false }) => ({
      data,
      isFetching
    });
    
    export default connect(
      mapStateToProps,
      { fetchPosts }
    )(WithCustomMiddleware);
    

    Redux 미들웨어가 어떻게 작동하는지 잘 모르시겠습니까? custom redux middleware 생성 방법에 대한 이 짧은 자습서를 확인하십시오.

    이제 React에서 데이터 가져오기를 처리하는 방법을 알았습니다! ✋


    지원하다

    기사를 즐겼습니까? Twitter에서 요약 스레드를 공유합니다.









    노드스쿨


    @nordschool






    👌- Standalone- HOC- With Hooks- Render Props- Redux Custom MiddlewareTHREAD... 👇에서 가장 일반적인 API 데이터 가져오기 패턴


    오전 08:38 - 2019년 10월 23일





    1

    2





    더 나은 코드 월요일 뉴스레터

    내 뉴스레터를 좋아할 수도 있습니다. 아이디어는 매주 월요일 3개의 웹 개발 팁을 공유하는 것입니다.

    저의 목표는 작문 실력을 향상시키고 최대한 많은 지식을 공유하는 것입니다. 지금까지 수백 명의 개발자가 구독했고 좋아하는 것 같습니다.

    내가 어떤 종류의 물건을 공유하는지 감을 잡으려면 previous newsletter issuessubscribe 을 확인하십시오.

    좋은 웹페이지 즐겨찾기