sakamotodd
@sakamotodd (坂本)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

useStateの無限ループのエラー解消法について

useStateの無限ループのエラー解消法について

Next.jsでMaterial-uiのCardコンポーネントを使用しmapで複数のカードを作る際にエラーが発生しました。
解決方法を教えてください。

発生している問題・エラー

mapで複数のアコーディオンボタンを作成した際に、useStateで無限エラーが発生します。
下記は一部のソースコードを抜粋したものです。
useStateでfalseを格納し、ボタンをクリックした際に反転するように記載しています。
その際に下記のエラーの対処方法がわかりません。

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
index.jsx
export function ProfileCard() {
  const [expanded, setExpanded] = useState(false);

  const plusButton = useCallback(() => {
    setExpanded(() => !expanded);
  }, []);

components/CardItem.jsx
      <CardActions disableSpacing>
        <IconButton
          onClick={plusButton}
          className={clsx(classes.expand, expanded && classes.expandOpen)}
          aria-label="plus"
          aria-expanded={expanded}
        >
          <ExpandMoreIcon />
        </IconButton>
      </CardActions>

      <Collapse in={expanded} timeout="auto" unmountOnExit>
        <CardContent>  
          ...
        </CardContent>
      </Collapse>

該当するソースコード

index.jsx
import { useCallback, useState } from "react";
import { CardItem } from "./CardItem";
import { Grid } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles((theme) => ({
  root: {
    margin: "16px",
    maxWidth: 445,
  },

  media: {
    height: 0,
    paddingTop: "50%",
  },
  expand: {
    transform: "rotate(0deg)",
    marginLeft: "auto",
    transition: theme.transitions.create("transform", {
      duration: theme.transitions.duration.leavingScreen,
    }),
  },
  expandOpen: {
    transform: "rotate(180deg)",
  },
}));

export function ProfileCard() {
  const classes = useStyles();
  const [expanded, setExpanded] = useState(false);
  const photos = ["kyoto.jpg", "nagasaki.jpg", "niigata.jpg"];

  const plusButton = useCallback(() => {
    setExpanded(() => !expanded);
  }, []);

  return (
    <>
      <Grid container>
        {photos.map((photo) => {
          return (
            <Grid item xs key={photo}>
              <CardItem
                classes={classes}
                plusButton={plusButton()}
                expanded={expanded}
                photo={photo}
              />
            </Grid>
          );
        })}
      </Grid>
    </>
  );
}
components/CardItem.jsx
import clsx from "clsx";
import {
  Card,
  CardHeader,
  CardMedia,
  CardContent,
  CardActions,
  Collapse,
  Avatar,
  IconButton,
  Typography,
} from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";


export function CardItem({classes,plusButton,expanded,photo}){
  return (
    <Card className={classes.root}>
      <CardHeader
        className={classes.avatar}
        title="タイトル"
        subheader="サブタイトル"
      />
      <CardMedia className={classes.media} image={photo} />
      <CardContent>
        <Typography
          variant="inherit"
          color="textSecondary"
          display="inline"
          component="p"
        >
          Kiyomizu 
        </Typography>
      </CardContent>
      <CardActions disableSpacing>
        <IconButton
          onClick={plusButton}
          className={clsx(classes.expand, expanded && classes.expandOpen)}
          aria-label="plus"
          aria-expanded={expanded}
        >
          <ExpandMoreIcon />
        </IconButton>
      </CardActions>
      <Collapse in={expanded} timeout="auto" unmountOnExit>
        <CardContent>
          <Typography paragraph variant="h6" align="center">
            content
          </Typography>
          <Typography color="textSecondary">
            Kiyomizu 
          </Typography>
        </CardContent>
      </Collapse>
    </Card>
 );
}

自分で試したこと

下記のサイトを参考にしました。
https://qiita.com/katsuomi/items/5a062dc1e4d0ab64ae21

0

3Answer

index.jsx
<CardItem
  classes={classes}
  plusButton={plusButton()} // <=!!!

ここは

CardItem
<IconButton onClick={plusButton} ...

onClickの関数を期待しているはずなので、

index.jsx
<CardItem
  classes={classes}
  plusButton={() => {plusButton()}}

または

index.jsx
<CardItem
  classes={classes}
  plusButton={plusButton}

です。

plusButton={plusButton()}では、plusButtonの実行をして結果を渡しています。
この実行が運悪くstateの変更をしているので、

描画しては変更、変更なんで描画、描画しては変更

というループに入ります。

1Like

Comments

  1. @sakamotodd

    Questioner

    上記通りに訂正した後解決しました。
    ありがとうございます。

Comments

Your answer might help someone💌