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!

map処理中のボタン押下時のイベントについて

解決したいこと

Next.jsでuseStateの勉強をしています。
mapでCardコンポーネントの中にアコーディオンボタンをで折り畳みすることを実現したいと思っています。
しかし、下記の画像の左のアコーディオンボタンを押下した際に、他のカードも展開してしまいます。
一つのカードコンポーネントに対して、ボタンを押下した際のイベントを発生させたいのですがやり方が分かりません。
上記について教えてください。

image.png

image.png

該当するソースコード

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

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

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

  //アコーディオンボタンを押下時の処理
  const heartButton = useCallback(() => {
    setHeart((heart) => !heart);
  }, []);

  return (
    <>
      <Grid container>
        {photos.map((photo) => {
          return (
            <Grid item xs key={photo}>
              <CardItem
                plusButton={plusButton}
                heartButton={heartButton}
                expanded={expanded}
                heart={heart}
                photo={photo}
              />
            </Grid>
          );
        })}
      </Grid>
    </>
  );
}
CardItem.jsx
import clsx from "clsx";
import {Card,CardHeader,CardMedia,CardContent,CardActions,Collapse,IconButton,Typography,} 
 from "@material-ui/core";
import FavoriteIcon from "@material-ui/icons/Favorite";
import ShareIcon from "@material-ui/icons/Share";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import useStyles from "./styles/useStyles";


export function CardItem({plusButton,heartButton,expanded,heart,photo,}) {
  const classes = useStyles();

  return (
    <Card className={classes.root}>
      <CardHeader
        className={classes.avatar}
        title="aaaaa"
        subheader="November"
        action={
          <IconButton aria-label="settings">
            <MoreVertIcon />
          </IconButton>
        }
      />
      <CardMedia className={classes.media} image={photo} />
      <CardContent>
        <Typography
          variant="inherit"
          color="textSecondary"
          display="inline"
          component="p"
        >
          Kiyomizu 
        </Typography>
      </CardContent>
      <CardActions disableSpacing>
        <IconButton
          onClick={heartButton}
          className={heart && classes.favorites}
          aria-label="favorites"
        >
          <FavoriteIcon />
        </IconButton>
        <IconButton aria-label="share">
          <ShareIcon />
        </IconButton>
        <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 is one of Kyoto's leading tourist destinations, and its
          illumination is also popular, so you will have to wait in line during
          the season, but there are still scenery worth seeing. This time I
          would like to introduce its charm.
          </Typography>
        </CardContent>
      </Collapse>
    </Card>
  );
}

自分で試したこと

useStateの初期値に配列を入れ、falseをいれるように書き換える必要がありますでしょうか?
それとも別の方法がありますでしょうか?
下記のサイトを参考にしましたがやり方が分かりませんでした。
https://ja.reactjs.org/docs/lists-and-keys.html

0

3Answer

plusButtonを親側で持って、同じものを子それぞれに渡しているので、動きとしては正常だと思います。
子がそれぞれの独立した動きにさせたいなら、plusButtonはむしろ要らないような。

親からのplusButtonexpandedは一旦無視して(propsから消す)、CardItem内で

const [expanded, expand] = React.setState(false)

...

onClick={() => expand(prev => !prev)}

としたらどうなりますか?

1Like

plusButtonで処理を共通化していたのですね..
上記で試した場合、下記のエラーが出ましので、

TypeError: react__WEBPACK_IMPORTED_MODULE_8___default.a.setState is not a function or its return value is not iterable

下記に変更した際に解決しました。
教えていただきありがとうございます。

CardItem.jsx
const [expanded, setExpanded] = useState(false);
...
onClick={() => setExpanded(prev => !prev)}

0Like

そのエラーはただ単に

import {useState} from "react"

という宣言だったんで、Reactっていうオブジェクトがなかっただけでは?

0Like

Your answer might help someone💌