21
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【初心者】#2 React axiosでAPI データ取得

Last updated at Posted at 2021-01-17

APIからJSONデータを取得して表示する

取得前回はMaterial-UIで見た目を整えましたが、
データをハードコードしただけなので、APIからデータを取得してみたいと思います。
useStateを使います。

今後、Python DjangoでAPIサーバー作ってAPIサーバーを作ってみたいと思いますが、最初はJSON Placeholderというサービスで取得したいと思います。

コードは前回の引き続きになります。

:pushpin: React & Material UI & Django RestFramework記事一覧

内容
part1 React Material UIを使ってみる
part2 【React】#2 React axiosでAPI データ取得 ← ココ
part3 【React】 #3 ルーティング・ページ遷移 react-router-dom
part4 【Python】 Django REST Framework ReactとAPIサーバー繋ぐ

使用環境

  • axios 0.21.1
  • react 17.0.1
  • material-ui 4.11.2

axiosインストールして使う

$ npm install axios

画面を読み込んだ時に一度だけ、APIでデータを取得するようにします。
まず、必要なものをインポートして

material-react/src/components/Content.js
import React, {useState, useEffect} from 'react';
import axios from 'axios';

const [post, setPosts] = useState([])
オブジェクト配列で取得してくるので初期値は配列を入れていきます。

JSONはアクセスしてデータを確認してみてください。

https://jsonplaceholder.typicode.com/posts

GETで取得するとこのようなデータです。

.json
{
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  }

のようなデータが取得できます。

useStateで一回だけ最初にAPI叩いてデータ取得

データは

.js
useEffect(() => {
        axios.get('https://jsonplaceholder.typicode.com/posts')
        .then(res => {
            setPosts(res.data)
        })
    }, [])

のようにすればGETで取得して、
const [post, setPosts] = useState([])
のようにしたので、postに格納されます。

※ 注意ですが、最後の, []がないと何度も何度もデータを取得することになるので注意!APIリクエスト回数で課金されるサービスで、これで作ってしまったら大変です

この空の配列は、ページ開いたら一度だけ。引数をあげなければ、何か処理起こったら毎回動きます。
条件を指定して、処理を実行することもできます。

結果的にこんな感じで処理を書きました。

material-react/src/components/Content.js
import React, {useState, useEffect} from 'react';
import axios from 'axios';

import { Grid } from '@material-ui/core'
import BodyCard from './BodyCard'

const cardContent = 
    {
        avatarUrl: "https://joeschmoe.io/api/v1/random",
        imageUrl: "https://picsum.photos/150"
    }

function Content() {
    const [post, setPosts] = useState([])

    useEffect(() => {
        axios.get('https://jsonplaceholder.typicode.com/posts')
        .then(res => {
            setPosts(res.data)
        })
    }, [])
    const getCardContent = getObj => {
        const bodyCardContent = {...getObj, ...cardContent};
        return (
            <Grid item xs={12} sm={4} key={getObj.id}>
                <BodyCard {...bodyCardContent} />
            </Grid>
        );
    };
    return (
        <Grid container spacing={2}>
            {post.map(contentObj => getCardContent(contentObj))}
        </Grid>
    )
}

export default Content

アバターとイメージは前回のままです。
APIで取得したデータをスプレッド演算子
const bodyCardContent = {...getObj, ...cardContent};で結合させて、渡しているだけです。

material-react/src/components/BodyCard.js
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import CardHeader from '@material-ui/core/CardHeader';
import Avatar from '@material-ui/core/Avatar';
import IconButton from '@material-ui/core/IconButton';
import StarBorderOutlinedIcon from '@material-ui/icons/StarBorderOutlined';
import { CardMedia } from '@material-ui/core';

const useStyles = makeStyles({
    bullet: {
      display: 'inline-block',
      margin: '0 2px',
      transform: 'scale(0.8)',
    },
    title: {
      fontSize: 14,
    },
    pos: {
      marginBottom: 12,
    },
});


function BodyCard(props) {
    const { userId, id, title, body, avatarUrl, imageUrl } = props;
    const classes = useStyles();
    const bull = <span className={classes.bullet}></span>;
    return (
        <Card variant="outlined">
            <CardHeader
                avatar={<Avatar src={avatarUrl} />}
                action={
                <IconButton aria-label="settings">
                    <StarBorderOutlinedIcon />
                </IconButton>
                }
                title={title}
            />
            <CardMedia style={{ height: "150px" }} image={imageUrl} />
            <CardContent>
            <Typography variant="body2" component="p">
                {body}
            </Typography>
            </CardContent>
            <CardActions>
            <Button size="small">詳細をみる</Button>
            </CardActions>
        </Card>
    );
}

export default BodyCard

そして、渡されたデータを受け取って、タイトルと、本文を表示に使っています。

スクリーンショット 2021-01-15 19.28.45.png

文字数によって、カードの高さずれてますね…
高さ指定、一定文字数以上は消すためにCSS追加します。

makeStyleの書き換え

Material-UIでCSSを書いてみます。慣れるまでなかなか扱いにくそうですね・・・

material-react/src/components/BodyCard.js
const useStyles = makeStyles({
.
.
.
    cHeader: {
        height: '50px',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
        "& .MuiCardHeader-content": {
            overflow: 'hidden'
        }
    },
    cContent: {
        height: '200px',
        overflow: 'hidden'
    }
});


function BodyCard(props) {
    const { userId, id, title, body, avatarUrl, imageUrl } = props;
    const classes = useStyles();
    const bull = <span className={classes.bullet}></span>;
    return (
        <Card variant="outlined">
            <CardHeader
                avatar={<Avatar src={avatarUrl} />}
                action={
                <IconButton aria-label="settings">
                    <StarBorderOutlinedIcon />
                </IconButton>
                }
                className={classes.cHeader}
                title={title}
            />
            <CardMedia style={{ height: "150px" }} image={imageUrl} />
            <CardContent className={classes.cContent}>
            <Typography variant="body2" component="p">
                {body}
            </Typography>
            </CardContent>
            <CardActions>
            <Button size="small">詳細をみる</Button>
            </CardActions>
        </Card>
    );
}

export default BodyCard

苦戦しながら、やったらこんな感じでうまくいきました。
"& .MuiCardHeader-content"は生成されたクラスで、検証ツールで調べて指定してます。&はSCSS的と同じ感じですね。知らないと、ハマりそう。

あと、textOverflowはcssだとtext-over-flowですが、-をなくして、キャメルケースにする必要があるのでご注意ください。

スクリーンショット 2021-01-15 20.41.19.png

次回は

次回は、react-router-domを導入してルーティングを設定します。
画像の「詳細をみる」から詳細ページを表示する機能を追加します。

:pushpin: React & Material UI & Django RestFramework記事一覧

内容
part1 React Material UIを使ってみる
part2 【React】#2 React axiosでAPI データ取得 ← ココ
part3 【React】 #3 ルーティング・ページ遷移 react-router-dom
part4 【Python】 Django REST Framework ReactとAPIサーバー繋ぐ
21
13
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?