buto > /dev/null

だいたい急に挑戦してゴールにたどり着かずに飽きる日々です

react setState反映タイミングを調べてみる

昨日の記事でMaterial-UIのTableを使ってオブジェクト配列を持つstate変数の値を表示しましたが

最初にsetState()でセットしたオブジェクトが表示されていませんでした

components/DataTable.tsx

useItem.tsxで取得したstate変数をDataTable.tsxで定義したstate変数にセットし直しています

useEffectでログ出力を追加しました

import {useState, useEffect} from 'react';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import useItem from '../logic/useItem';
// オブジェクト型をインポート
import type {Item} from '../logic/useItem';

function DataTable() {

  const {itemList, setItemList, addItem} = useItem();
  const [saleItem, setSaleItem] = useState<Item[]>([]);

  const sample: Item = {
    id: 0,
    category: 'none',
    item: 'sampleItem',
    price: 0
  };
  let array: Item[] = [];
  array.push(sample);

  useEffect(() => {
    setItemList(array);
    console.log('useEffect([])');
    console.log('itemList : ' + JSON.stringify(itemList));
    console.log('**************');
    // regularItem();
  }, []);

  useEffect(() => {
    setSaleItem(itemList);
    console.log('useEffect([itemList])');
    console.log('itemList : ' + JSON.stringify(itemList));
    console.log('saleItem' + JSON.stringify(saleItem));
    console.log('**************');
  }, [itemList]);

  const regularItem = () => {
    addItem('sweets', 'chocolate', 250);
    addItem('sweets', 'muffin', 390);
    addItem('snack', 'potato chips', 180);
    addItem('beverage', 'cola', 120);
  };

  return (
    <TableContainer>
      <Table sx={{ maxWidth: 250 }}>
        <TableHead>
          <TableRow>
            <TableCell>id</TableCell>
            <TableCell>category</TableCell>
            <TableCell>item</TableCell>
            <TableCell>price(¥)</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {saleItem.map(item => (
            <TableRow key={item.id}>
              <TableCell align="right">{item.id}</TableCell>
              <TableCell>{item.category}</TableCell>
              <TableCell>{item.item}</TableCell>
              <TableCell align="right">{item.price}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

export default DataTable;

logic/useItem.tsx ※昨日と同じです

import {useState} from 'react';

function useItem() {

  const [itemList, setItemList] = useState<Item[]>([]);

  const addItem = (category:string, item:string, price:number) => {
    const el:Item = {
      id: itemList.length,
      category: category,
      item: item,
      price: Math.ceil(price * 1.1)
    };

    const current = itemList;
    current.push(el);
    setItemList(current);
  };

  return {itemList, setItemList, addItem};
};

export default useItem;

/* 商品のオブジェクト型 */
type Item = {
  id: number,
  category: string,
  item: string,
  price: number
};

export type {Item};

初期表示で変数sampleの値が表示されています f:id:butorisa:20210922234325p:plain

ログを読み解くと、、、

  1. 第2引数が[]のuseEffect()が実行され、useItem.tsxのitemListに値がセットされる
  2. この時点でのitemListは値がセットされる前の状態なので[]
  3. 第2引数が[itemList]のuseEffect()が実行される
  4. saleItemはもちろん[]
  5. itemListが変更されたので第2引数が[itemList]のuseEffect()が実行される
  6. DataTable.tsxのsaleItemに値がセットされる
  7. itemListは値がセットされた後の状態になっている
  8. この時点でのsaleItemは値がセットされる前の状態なので[]

state変数の変更反映はレンダリング後なのでstate変数を他の変数に代入し直して表示させる必要があります