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

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

新規登録して質問してみよう
ただいま回答率
85.48%
JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

2128閲覧

Reactで、JSONファイルの表示の仕方が分からない

Shmupeiii

総合スコア105

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2021/11/24 15:26

前提・実現したいこと

https://jsonplaceholder.typicode.com/

上記サイトからpostsを取得して、postsのidをkeyにしたオブジェクトを作成する。
valueには、apiから取得したkeyが対応するpostsのオブジェクトと、postsIdが一致するcommentsの配列を入れたい。
それを50投稿文作り以下のようなJSONファイルの形式で出力したい。

JavaScript

1{ 2 "1": { 3 "userId": 1, 4 "id": 1, 5 "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", 6 "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto", 7 "comments": [ 8 { 9 "postId": 1, 10 "id": 1, 11 "name": "id labore ex et quam laborum", 12 "email": "Eliseo@gardner.biz", 13 "body": "laudantium enim quasi est quidem magnam voluptate ipsam eos\ntempora quo necessitatibus\ndolor quam autem quasi\nreiciendis et nam sapiente accusantium" 14 }, 15 { 16 "postId": 1, 17 "id": 2, 18 "name": "quo vero reiciendis velit similique earum", 19 "email": "Jayne_Kuhic@sydney.com", 20 "body": "est natus enim nihil est dolore omnis voluptatem numquam\net omnis occaecati quod ullam at\nvoluptatem error expedita pariatur\nnihil sint nostrum voluptatem reiciendis et" 21 }, 22 { 23 "postId": 1, 24 "id": 3, 25 "name": "odio adipisci rerum aut animi", 26 "email": "Nikita@garfield.biz", 27 "body": "quia molestiae reprehenderit quasi aspernatur\naut expedita occaecati aliquam eveniet laudantium\nomnis quibusdam delectus saepe quia accusamus maiores nam est\ncum et ducimus et vero voluptates excepturi deleniti ratione" 28 } 29 ] 30 }, 31 32}

そして、postsとcommentのapiを取得し、画面に表示することができました。
イメージ説明
postの表示画像

イメージ説明
commentの表示画像

しかし以下の点が分かりません。
・valueには、apiから取得したkeyが対応するpostsのオブジェクトと、postsIdが一致するcommentsの配列を入れ方
・50投稿文ループさせる方法がわからない(mapやwhileの指定が分からず)
・JSON形式にオブジェクトを入れる方法(JSONファイルを出力するにはnode.jsを使う方法しか出てこず分からない)

全てじゃなくて大丈夫なので、解決方法やヒントなどを教えていただきたいです。
よろしくお願いいたします。

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

JavaScript

1 2import React, {useState, useEffect} from 'react' 3import axios from 'axios' 4 5const ApiFetch = () => { 6 7 const [posts, setPosts] = useState([]) 8 9 useEffect(() => { 10 axios.get('https://jsonplaceholder.typicode.com/posts') 11 .then(res => { 12 setPosts(res.data) 13 }) 14 }, []) 15 16 const [comments, setComments] = useState([]) 17 18 useEffect(() => { 19 axios.get('https://jsonplaceholder.typicode.com/comments') 20 .then(res2 => { 21 setComments(res2.data) 22 }) 23 }, []) 24 25 return ( 26 <div> 27 <ul> 28 29 30 { 31 posts.map((post) => ( 32 <div> 33 <h1>{ post.id }</h1> 34 <ul> 35<li>"userId": {post.id}</li> 36<li>"userId": {post.title}</li> 37<li>"userId": {post.body}</li> 38 </ul> 39 </div> 40 41 ) 42 ) 43 } 44 { 45 comments.map((comment) => ( 46 <div> 47 <h1>{ posts.id }</h1> 48 <ul> 49<li>"userId": {comment.id}</li> 50<li>"title": {comment.title}</li> 51<li>"body": {comment.body}</li> 52 </ul> 53 </div> 54 ) 55 ) 56} 57 </ul> 58 59 </div> 60 ) 61} 62 63export default ApiFetch

試したこと

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

ReactはこちらのPackage.jsonを使っています。

JavaScript

1 2{ 3 "name": "react-file", 4 "version": "0.1.0", 5 "private": true, 6 "dependencies": { 7 "@testing-library/jest-dom": "^5.11.4", 8 "@testing-library/react": "^11.1.0", 9 "@testing-library/user-event": "^12.1.10", 10 "axios": "^0.24.0", 11 "react": "^17.0.2", 12 "react-dom": "^17.0.2", 13 "react-scripts": "4.0.3", 14 "web-vitals": "^1.0.1" 15 }, 16 "scripts": { 17 "start": "react-scripts start", 18 "build": "react-scripts build", 19 "test": "react-scripts test", 20 "eject": "react-scripts eject" 21 }, 22 "eslintConfig": { 23 "extends": [ 24 "react-app", 25 "react-app/jest" 26 ] 27 }, 28 "browserslist": { 29 "production": [ 30 ">0.2%", 31 "not dead", 32 "not op_mini all" 33 ], 34 "development": [ 35 "last 1 chrome version", 36 "last 1 firefox version", 37 "last 1 safari version" 38 ] 39 } 40} 41

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

質問にある以下の3点

しかし以下の点が分かりません。

・valueには、apiから取得したkeyが対応するpostsのオブジェクトと、postsIdが一致するcommentsの配列を入れ方
・50投稿文ループさせる方法がわからない(mapやwhileの指定が分からず)
・JSON形式にオブジェクトを入れる方法(JSONファイルを出力するにはnode.jsを使う方法しか出てこず分からない)

のうち、はじめの2点を
(1) postsとcommentsをマージしたオブジェクトを作る
3点目を
(2)ファイルのダウンロード方法
として回答します。

(1) postsとcommentsをマージしたオブジェクトを作る

そういうときは、(まずは) lodashを使って切り抜けたいです。(※ 備考も参照ください)

javascript

1const merge = (posts, comments, maxPostId) => { 2 3 const filteredComments = comments.filter(({ postId }) => postId <= maxPostId); 4 5 const groupByPost = _(filteredComments) 6 .groupBy('postId') 7 .mapValues( 8 (comments, postId) => ({ 9 ...posts.find(({ id }) => id === +postId), 10 comments 11 }) 12 ) 13 .value(); 14 15 return groupByPost; 16} 17

という関数を作っておいて、

javascript

1const mergedPosts = merge(posts, comments, 50); 2setPosts(mergedPosts);

とすればいけます。

細かいところの注意点ですが、.groupByでpostIdによるグループ化をした後の.mapValues に与えている関数の第二引数 postId には、postId が文字列で渡ってきます。なので

javascript

1...posts.find(({ id }) => id === +postId),

のところで、+postId で使っている単項加算+を忘れて単に

javascript

1...posts.find(({ id }) => id === postId),

としてしまうと、findが失敗してpostの内容がマージされなくなります。

備考: 最近、lodashは重すぎるといった意見(例: mizchiさん)により、現場によっては、lodashの各メソッドを自分で書くこともありますが、その場合は以下が参考になります。

(2)ファイルのダウンロード方法

マージして作成したオブジェクトをJSONファイルにしてダウンロードするには、Blobを使えばよいかと思います。たとえば、前掲の動作確認のサンプル の中で、 Promise.all が成功したときの処理に以下を追加します。

diff

1 Promise.all(fetchers).then(([{ data: posts }, { data: comments }]) => { 2 const mergedPosts = merge(posts, comments, 50); 3 setPosts(mergedPosts); 4 5+ // ファイルとしてダウンロード 6+ const data = new Blob([JSON.stringify(mergedPosts, null, 4)], {type: 'application/json'}); 7+ const link = document.createElement('a'); 8+ link.href = window.URL.createObjectURL(data); 9+ link.setAttribute('download', 'mergedPosts.json'); 10+ link.click(); 11 });

追記

上記の (1) で、lodash を使うコードを回答しましたが、lodashを使わない merge関数のコード例も挙げておきます。

javascript

1const merge = (posts, comments, maxPostCount) => 2 posts.slice(0, maxPostCount) 3 .reduce((obj, post) => { 4 obj[post.id] = { 5 ...post, 6 comments: comments.filter(({ postId }) => post.id === postId) 7 }; 8 return obj; 9 }, {});

先の(1)で挙げたコードでは、mergeの第三引数を、postIdの最大値maxPostIdにしていましたが、postの個数の上限値maxPostCount に変更しました。

追記2

上記の回答で、codepenに挙げたコード(のlodashを使うほう)をお手元で試すには、以下の手順で実行できるかと思います。

(1) 依存モジュールのインストール

shell

1npm install lodash

axios はすでにインスト−ルされているかと思いますが、まだであれば、

shell

1npm install axios

(2) src/components/ApiFetch.js を作成

以下のように作成します。

jsx

1import { useEffect, useState } from 'react'; 2import axios from 'axios'; 3import _ from 'lodash'; 4 5const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts'; 6const COMMENTS_URL = 'https://jsonplaceholder.typicode.com/comments'; 7 8const merge = (posts, comments, maxPostId) => { 9 10 const filteredComments = comments.filter(({ postId }) => postId <= maxPostId); 11 12 const groupByPost = _(filteredComments) 13 .groupBy('postId') 14 .mapValues( 15 (comments, postId) => ({ 16 ...posts.find(({ id }) => id === +postId), 17 comments 18 }) 19 ) 20 .value(); 21 22 return groupByPost; 23} 24 25const ApiFetch = () => { 26 27 const [posts, setPosts] = useState({}); 28 29 useEffect(() => { 30 const urls = [POSTS_URL, COMMENTS_URL]; 31 const fetchers = urls.map(url => axios.get(url)); 32 Promise.all(fetchers).then(([{ data: posts }, { data: comments }]) => { 33 const mergedPosts = merge(posts, comments, 50); 34 setPosts(mergedPosts); 35 }); 36 }, []); 37 38 return ( 39 <pre id="posts"> 40 {JSON.stringify(posts, null, 4)} 41 </pre> 42 ); 43} 44 45export default ApiFetch; 46

(3) App.jsからApiFetchをimport

これで、App.jsで、

javascript

1import ApiFetch from './components/ApiFetch';

とすることで、Appの返すJSXの中で、<ApiFetch /> をどこかに書けば、動作確認できると思われます。

投稿2021/11/24 19:53

編集2021/11/25 14:02
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Shmupeiii

2021/11/25 00:19

回答ありがとうございます。 丁寧に解説してくださり、理解できました。 もう一点質問なんですが、これをもし通常のreactフォルダで、ApiFetch.jsに上記のコードを書き、App.jsに渡して、index.jsで表示させるとなると、ApiFetchをexportすれば表示できるという認識で合っていますか。
退会済みユーザー

退会済みユーザー

2021/11/25 00:46

コメントありがとうございます。 はい。できますよ。ApiFetch.js を ---- import { useEffect, useState } from 'react'; const POSTS_URL = 'https://jsonplaceholder.typicode.com/posts'; const COMMENTS_URL = 'https://jsonplaceholder.typicode.com/comments'; const merge = (posts, comments, maxPostId) => { ・・・ } const ApiFetch = () => { ・・・ } export default ApiFetch; ---- としておいて、index.js では --- ・・・ import ApiFetch from './somewhere/ApiFetch' ・・・ const App = () => { ・・・ return ( <div className="app"> <ApiFetch /> </div> ); } ReactDOM.render( <App />, document.getElementById("root") ); --- のようにすればよいかと思います。
Shmupeiii

2021/11/25 12:47

連絡ありがとうございます。 コードありがとうございます。 上記コードを貼り付けて試してみたんですが、 Failed to compile. src/components/ApiFetch.js Line 10:25: '_' is not defined no-undef Line 29:40: 'axios' is not defined no-undef Search for the keywords to learn more about each error. のようなエラーが出ました。 axiosをnpmでインストールしても変化がなかったんですが、 何か不足しているという感じでしょうか。 質問が多く申し訳ありません。 よろしくお願いします。
退会済みユーザー

退会済みユーザー

2021/11/25 14:05

CodePen では依存モジュールを import する行を書かなかったりして、ローカルとはちょっと勝手が違う部分があり、そのままコピペでは使えないので、お手元の環境でApiFetch.js を作成する場合について、追記2 を回答に書きました。これを試してみていただけますでしょうか?
Shmupeiii

2021/11/26 02:31

回答ありがとうございます。 全て解決できました。丁寧な解説ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問