質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.10%

APIで取得したデータの一覧を.mapで表示させようとすると、エラーが発生する

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 433

wisheebell

score 7

前提・実現したいこと

railsサーバーからapiでデータを取得し、Reactで一覧を表示する。

発生している問題・エラーメッセージ

Uncaught TypeError: _this3.state.posts.map is not a function

該当のソースコード

import React, { Component } from 'react'
import { withRouter } from 'react-router';
import axios from 'axios';
import Typography from '@material-ui/core/Typography'
import Post from '../component/Post';

class Index extends Component {
  constructor(props) {
    super(props)
    this.state = {
      posts: []
    }
  }


  componentDidMount() {
    axios
    .get('https://6ccf1b5402214ffdb31120bbd0a35c58.vfs.cloud9.us-east-2.amazonaws.com/api/v1/posts')
    .then((results) => {
      console.log(results)
      this.setState({ posts: results.data })
    })
    .catch((data) =>{
      console.log(data)
    })
  }

  render(){
    return(
      <div>
        {(() => {
          if (this.state.posts != []) {
            this.state.posts.map( post => {
              return(
                <Post title={post.created_at} name='testUser' title={post.title} content={post.content} />
              )
            })
          } else {
            <Typography variant="h1" component="h2">
              まだなにも投稿されていません。
            </Typography>
          }
        })()}
      </div>
    );
  }
}

export default Index;

試したこと

stack overflowにて似たような状況の質問を見つけたが、その質問の回答の通りに試しても正常に動作しなかった。

補足情報(FW/ツールのバージョンなど)

cloud9
React.js v16.8.6

追記

回答のコメントより、改善したものを追記します。

import React, { Component } from 'react'
import { withRouter } from 'react-router';
import axios from 'axios';
import Typography from '@material-ui/core/Typography'
import Post from '../component/Post';

class Index extends Component {
  constructor(props) {
    super(props)
    this.state = {
      posts: []
    }
  }


  componentDidMount() {
    axios
      .get('https://6ccf1b5402214ffdb31120bbd0a35c58.vfs.cloud9.us-east-2.amazonaws.com/api/v1/posts')
      .then((results) => {
        console.log(results)
        this.setState({ posts: results.data })
      })
      .catch((data) =>{
        console.log(data)
      })
  }

  render(){
    return(
      <div>
        {(() => {
          if (this.state.posts.length > 0) {
            return (
              this.state.posts.map( (post, i) => {
                return(
                  <Post
                    key={i}
                    createdAt={post.created_at}
                    name='testUser'
                    title={post.title}
                    content={post.content}
                  />
                )
              })
            )
          } else {
            return (
              <Typography variant="h6" component="h2">
                まだなにも投稿されていません。
              </Typography>
              )
          }
        })()}
      </div>
    );
  }
}

export default Index;
import React, { Component } from 'react';
import { makeStyles, withStyles } 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';

class Post extends Component {
  render(){
    const classes = makeStyles({
      card: {
        minWidth: 275,
      },
      bullet: {
        display: 'inline-block',
        margin: '0 2px',
        transform: 'scale(0.8)',
      },
      title: {
        fontSize: 14,
      },
      pos: {
        marginBottom: 12,
      },
    });

    return (
      <Card className={classes.card}>
        <CardContent>
          <Typography className={classes.title} color="textSecondary" gutterBottom>
            {this.props.createdAt}
          </Typography>
          <Typography variant="h5" component="h2">
            {this.props.name}
          </Typography>
          <Typography className={classes.pos} color="textSecondary">
            {this.props.title}
          </Typography>
          <Typography variant="body2" component="p">
            {this.props.content}
          </Typography>
        </CardContent>
        <CardActions>
          <Button size="small">Learn More...</Button>
        </CardActions>
      </Card>
    );
  }
}

export default Post;
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • jun68ykt

    2019/08/24 03:53

    お手数ですが、axios でGETしているAPIのレスポンス もご質問に追記頂けますと、原因を特定しやすいかもしれません。

    キャンセル

  • wisheebell

    2019/08/24 11:47

    書き込みありがとうございます。
    レスポンスは以下のようになっています
    {data: {…}, status: 200, statusText: "", headers: {…}, config: {…}, …}
    config: {url: "https://6ccf1b5402214ffdb31120bbd0a35c58.vfs.cloud9.us-east-2.amazonaws.com/api/v1/posts", method: "get", headers: {…}, transformRequest: Array(1), transformResponse: Array(1), …}
    data:
    data: Array(1)
    0: {id: 6, content: "hogehoge", created_at: "2019-08-23T15:18:02.644Z", updated_at: "2019-08-23T15:18:02.644Z", title: "sample"}
    length: 1
    __proto__: Array(0)
    message: "loaded posts"
    status: "SUCCESS"
    __proto__: Object
    headers: {x-runtime: "0.024171", date: "Sat, 24 Aug 2019 02:45:17 GMT", referrer-policy: "strict-origin-when-cross-origin", x-permitted-cross-domain-policies: "none", etag: "W/"eeeef018be7df35fe8c9f5f2dbc842e9"", …}
    request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
    status: 200
    statusText: ""
    __proto__: Object

    ステータスが200でdataの内容も問題ないので、おそらく正常に受け取れていると思われます。

    キャンセル

  • jun68ykt

    2019/08/24 13:55

    レスポンスの追記、ありがとうございます。そうですね、レスポンスには問題ないようですね。他の部分に問題があると思われましたので、その点を修正したコードを回答しました。

    キャンセル

回答 2

checkベストアンサー

0

こんにちは

Indexコンポーネントについて、以下の4点
・if の条件を修正
・即時関数の return が欠けているので追加
・Post の prop title が重複しているので修正
・Post に key 追加
を修正して、下記のようにしてみるといかがでしょうか?

import React, { Component } from 'react'
import { withRouter } from 'react-router';
import axios from 'axios';
import Typography from '@material-ui/core/Typography'
import Post from '../component/Post';

class Index extends Component {
  constructor(props) {
    super(props)
    this.state = {
      posts: []
    }
  }


  componentDidMount() {
    axios
      .get('https://6ccf1b5402214ffdb31120bbd0a35c58.vfs.cloud9.us-east-2.amazonaws.com/api/v1/posts')
      .then((results) => {
        console.log(results)
        this.setState({ posts: results.data })
      })
      .catch((data) =>{
        console.log(data)
      })
  }

  render(){
    return(
      <div>
        {(() => {
          if (this.state.posts.length > 0) {
            return (
              this.state.posts.map( (post, i) => {
                return(
                  <Post
                    key={i}
                    createdAt={post.created_at}
                    name='testUser'
                    title={post.title}
                    content={post.content}
                  />
                )
              })
            )
          } else {
            return (
              <Typography variant="h1" component="h2">
                まだなにも投稿されていません。
              </Typography>
              )
          }
        })()}
      </div>
    );
  }
}

export default Index;

参考までに、上記の修正を確認するためのコードを、以下に作成しています。

以下は上記のレポジトリの説明です。

以下の手順で動作確認できます。

  • git clone https://github.com/jun68ykt/q207924.git
  • cd q207924
  • yarn install
  • yarn start

上記によって、以下のように表示されると思います。

イメージ説明

以下3点は補足です。

(1) 最初のコミット で yarn start させると確かにエラーにはなりますが、ご質問にある、 
Uncaught TypeError: _this3.state.posts.map is not a function 
というエラーは再現しませんでした。

(2) posts が空の配列である場合の「まだなにも投稿されていません。」のような表示をエンプティステートといいますが、エンプティステートを返すべきかどうかの判定をrenderの冒頭でやってしまって、該当していたら、早々とreturn させるという手も、個人的にはよく使います。その場合は以下のようにします。

(3) 上記の(2) の他に、エンプティステートを表示すべき場合と、そうでない場合の切り分けに、三項演算子を使うこともできます。

以上、参考になれば幸いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/08/24 18:00

    ちなみにご質問に追記いただきました Post のほうは特に問題なく、私の回答で作成したコードの Post にそのままコピペしても意図通り Material でデザインされたPostコンポーネントが表示されました。

    キャンセル

  • 2019/08/24 18:23

    上記の通りthis.setState({ posts: results.data.data })と修正したところ、正常に動作しました!
    わかりにくい質問でしたが、根気よくお付き合いいただきありがとうございます!

    キャンセル

  • 2019/08/24 18:27

    どういたしまして!解決したようで、よかったです 🎉

    キャンセル

0

Uncaught TypeError: _this3.state.posts.map は関数ではありません

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る