Skip to content

05 Fetching

JP Barbosa edited this page Apr 18, 2021 · 4 revisions

Fetching (Articles List)

Components

Components

Specify API URL

code ./.env
REACT_APP_API=https://typescript-crud-api.herokuapp.com
#REACT_APP_API=http://localhost:4000

Obs.: To run the API locally checkout TypeScript CRUD API.

Create useFetch

code ./src/hooks/useFetch.ts
import axios from 'axios';
import { useState, useEffect } from 'react';
import { Record } from '../interfaces/RecordEntities';

export const useFetch = <T extends Record>(path: string, options?: {}) => {
  const [records, setRecords] = useState<T[]>([]);

  const url = `${process.env.REACT_APP_API}/${path}`;

  useEffect(() => {
    const callFetchFunction = async () => {
        const res = await axios.get<T[]>(url, { params: options });
        setRecords(res.data);
    };
    callFetchFunction();
  }, [url, options]);

  return { records };
};

Add ArticleIndex to App

code ./src/App.tsx
...
import { ArticleIndex } from './pages/Article';

const App: React.FC = () => {
  ...

  const renderSwitch = () => {
    switch (page) {
      case Page.Articles:
        return <ArticleIndex />;
      ...
    }
  };

  ...
};

export default App;

Create ArticleIndex

code ./src/pages/Article/index.tsx
import { Article } from '../../interfaces/RecordEntities';
import { RecordIndex } from '../Record';
import { ArticleListItem } from './ListItem';

export const ArticleIndex: React.FC = () => {
  const apiOptions = { relations: ['author'] };

  return (
    <RecordIndex<Article>
      ListItem={ArticleListItem}
      apiPath="articles"
      apiOptions={apiOptions}
    />
  );
};

Specify RecordIndexProps

code ./src/interfaces/PagesProps.ts
export interface RecordIndexProps<T> {
  ListItem: React.FC<ListItemProps<T>>;
  apiPath: string;
  apiOptions: {};
}

Create RecordIndex

code ./src/pages/Record/index.tsx
import { Record } from '../../interfaces/RecordEntities';
import { RecordIndexProps } from '../../interfaces/PagesProps';
import { useFetch } from '../../hooks/useFetch';
import { RecordList } from './List';

export const RecordIndex = <T extends Record>({
  ListItem,
  apiPath,
  apiOptions,
}: RecordIndexProps<T>) => {
  const { records } = useFetch<T>(apiPath, apiOptions);

  return (
    <div className="page">
      <div className="content">
        <RecordList<T> ListItem={ListItem} records={records} />
      </div>
    </div>
  );
};

Specify RecordListProps

code ./src/interfaces/PagesProps.ts
...

export interface RecordListProps<T> {
  ListItem: React.FC<ListItemProps<T>>;
  records: T[];
}

Create RecordList

code ./src/pages/Record/List.tsx
import { Record } from '../../interfaces/RecordEntities';
import { RecordListProps } from '../../interfaces/PagesProps';
import { usePage } from '../../contexts/Page';

export const RecordList = <T extends Record>({
  ListItem,
  records,
}: RecordListProps<T>) => {
  const { page } = usePage();

  return (
    <div className="list">
      <h2>{page}</h2>
      <ul>
        {records.map((record) => (
          <li key={record.id}>
            <ListItem record={record} />
          </li>
        ))}
      </ul>
    </div>
  );
};

Specify ListItemProps

code ./src/interfaces/PagesProps.ts
...

export interface ListItemProps<T> {
  record: T;
}

Create ArticleListItem

code ./src/pages/Article/ListItem.tsx
import { Article } from '../../interfaces/RecordEntities';
import { ListItemProps } from '../../interfaces/PagesProps';

type IProps = ListItemProps<Article>;

export const ArticleListItem: React.FC<IProps> = ({ record }) => {
  return (
    <div>
      <div className="title">{record.title}</div>
      <div className="author">By {record.author?.name || 'Unknown'}</div>
    </div>
  );
};

Start React App

yarn start

Result

Result

Commit

git add .
git commit -m "Fetching"

Next step: Forms