前提・実現したいこと
IEX CloudのAPIを使って、シンプルな株のポートフォリオのウェブアプリケーションを作ろうとしています。
こちらのQiitaの記事を参考にさせて頂き、MATERIAL-UIを使って、テーブルのソート機能を実装しております。
発生している問題・エラーメッセージ
SortableTable.jsのSortableTable関数の中で、引数のdataをそのままconsole.logで出力しているのですが、一度目の表示で、値が更新されていないのが分かりました。どの様にすれば、React Hooksで、一度目から値を更新できるのでしょうか?
また、そもそもなぜログが無限に出てきてしまうのかも分かりません。
値の更新は、App.jsのaddStock関数で行っております。
また、React Hooksを使って、APIから取ってきた値をどの様に保存しておけばいいのかも分かっておりません。useStateでいくつか配列を用意しておいた場合、どの様に値を追加していけばいいのでしょうか?
該当のソースコード
App.js
React
1import React, { useState } from 'react'; 2import { makeStyles } from "@material-ui/core/styles"; 3import SortableTable from "./SortableTable.js"; 4 5 6const useStyles = makeStyles({ 7 table: { 8 width: "60%" 9 } 10}); 11 12 13export default function App() { 14 const [listOfStocks, setListOfStocks] = useState([]) 15 16 const [state, setState] = useState({ 17 symbol: [], 18 name: [], 19 price: [], 20 num_shares: [], 21 totalValue: [] 22 }); 23 24 function addStock() { 25 const url = urlForIEXCloud("aapl") 26 27 fetch(url).then((r) => r.json()).then((data) => { 28 setState({ 29 symbol: data.symbol, 30 name: data.companyName, 31 price: data.iexRealtimePrice, 32 share: 10, 33 totalValue: 200 34 }); 35 }); 36 } 37 38 function generateTableData(numRow) { 39 const data = [...Array(numRow).keys()].map(i => ({ 40 "Symbol": state.symbol, 41 "Company Name": state.name, 42 "Real Time Price": state.price, 43 "Number of Shares": state.share, 44 "Total Value": state.totalValue 45 })); 46 return data; 47 } 48 49 const classes = useStyles(); 50 addStock(); 51 const data = generateTableData(1); 52 53 return <SortableTable data={data} className={classes.table} />; 54} 55 56 57function urlForIEXCloud(symbol) { 58 const base_url = "https://cloud.iexapis.com/" 59 const version = "stable/" 60 const endpoint_path = `stock/${symbol}/quote` 61 const token = "pk_b56bbfd8b3d54c2c9417a27e8e35b64f" 62 const query_string_params = `?token=${token}` 63 return `${base_url}${version}${endpoint_path}${query_string_params}` 64}
SortableTable.js
import React, { useState } from "react"; import PropTypes from "prop-types"; import { makeStyles } from "@material-ui/core/styles"; import Table from "@material-ui/core/Table"; import TableHead from "@material-ui/core/TableHead"; import TableCell from "@material-ui/core/TableCell"; import TableRow from "@material-ui/core/TableRow"; import TableBody from "@material-ui/core/TableBody"; import TableSortLabel from "@material-ui/core/TableSortLabel"; import Paper from "@material-ui/core/Paper"; const useStyles = makeStyles({ root: { overflowX: "auto", whiteSpace: "nowrap" }, table: { tableLayout: "fixed" } }); export default function SortableTable({ data, className }) { console.log(data) const classes = useStyles(); const columns = Object.keys(data[0]); const [state, setState] = useState({ rows: data, order: "desc", key: columns[0] }); function handleClickSortColumn(column) { const isDesc = column === state.key && state.order === "desc"; const nextOrder = isDesc ? "asc" : "desc"; const sortRule = { asc: [1, -1], desc: [-1, 1] }; const sortedRows = state.rows.slice().sort((a, b) => { if (a[column] > b[column]) { return sortRule[nextOrder][0]; } else if (a[column] < b[column]) { return sortRule[nextOrder][1]; } else { return 0; } }); setState({ rows: sortedRows, order: nextOrder, key: column }); } return ( <div className={className}> <Paper className={classes.root}> <Table size="medium" className={classes.table}> <TableHead> <TableRow> {columns.map((column, colIndex) => ( <TableCell align={isNaN(state.rows[0][column]) ? "left" : "right"} key={`table-header-col-${colIndex}`} sortDirection={state.key === column ? state.order : false} > <TableSortLabel active={state.key === column} direction={state.order} onClick={() => handleClickSortColumn(column)} > {column} </TableSortLabel> </TableCell> ))} </TableRow> </TableHead> <TableBody> {state.rows.map((row, rowIndex) => ( <TableRow hover key={`table-row-row-${rowIndex}`}> {Object.keys(row).map((key, colIndex) => ( <TableCell align={isNaN(row[key]) ? "left" : "right"} key={`table-row-${rowIndex}-col-${colIndex}`} > {row[key]} </TableCell> ))} </TableRow> ))} </TableBody> </Table> </Paper> </div> ); } SortableTable.propTypes = { data: PropTypes.arrayOf(PropTypes.object).isRequired, className: PropTypes.string, };
補足情報(FW/ツールのバージョンなど)
よろしくお願い致します。
一度目の表示はまだ API から結果が帰ってきてないためで、「ロード中」みたいなのを表示するのがいいと思います。
ログが無限に出てくるのは、表示内容を更新するたびに App 関数が実行されて fetch が呼ばれるためで、API を繰り返し呼びすぎてますね。useEffect を使わないとやばいです。