【问题标题】:Using useState to update style of the current object that is clicked使用 useState 更新当前被点击对象的样式
【发布时间】:2021-08-23 16:37:20
【问题描述】:

目前,我的页面中有卡片视图的手风琴样式。当用户点击一张卡片时,卡片的颜色应该随着它的字体颜色而改变。目前要做到这一点,我正在使用 setState 和条件渲染来更改正在工作的点击卡片的颜色,但它正在更改我所有卡片的颜色,甚至是未点击的卡片。如何制作它,使其仅针对当前点击的卡片进行更改。

代码:

const [clicked, setClicked] = useState(false);
<div className="row">
  {items &&
    items.map((item) => (
      <div className="col-md-6 faqpadding">
        <Accordion
          className="cardHeight"
          style={{
            backgroundColor: clicked ? "#fff" : "#0e2043",
            boxShadow: "0px 4px 22px 9px rgba(0, 0, 0, 0.17)",
            border: "1px solid #919191",
            color: clicked ? "#0e2043" : "#E3AB50",
            padding: "20px",
          }}
        >
          <AccordionSummary
            expandIcon={<ExpandMoreIcon />}
            aria-controls="panel1a-content"
            id="panel1a-header"
            onClick={() => setClicked(!clicked)}
          >
            <Typography
              className={classes.heading}
              style={{ textAlign: "center" }}
            >
              {item.Title}
            </Typography>
          </AccordionSummary>
          <AccordionDetails>
            <Typography>{item.Content}</Typography>
          </AccordionDetails>
        </Accordion>
      </div>
    ))}
</div>

代码沙盒:https://codesandbox.io/s/nifty-nash-ps8m7?file=/src/App.tsx

【问题讨论】:

  • 请尝试创建最小代码来重现问题。我建议你用 li 元素替换你的代码。

标签: javascript reactjs


【解决方案1】:

您使用相同的状态来有条件地更改样式,因此当状态更改时,样式将应用于两张卡片。 要解决此问题,您可以创建一个卡片组件并通过道具将所有文本内容移动到它,然后使用相同的组件两次。 我试过这样做,这是最终代码的链接-https://codesandbox.io/s/immutable-rgb-us349

【讨论】:

    【解决方案2】:

    您正在使用具有 boolean 类型的单个状态变量来尝试跟踪多个项目,这永远不会起作用。

    您可以拥有一个 array 的项目,这些项目应该具有 clicked 状态,例如通过存储它们的索引或一些唯一 ID,例如

    const [clicked, setClicked] = useState([])
    

    然后单击,从数组中添加(或删除):

    onClick={() => handleClick(index)}
    // or
    onClick={() => handleClick(item.id)}
    
    const handleClick = (index) => {
       if(clicked.includes(index)) {
           // remove from 'clicked' array
       } else {
           // add to 'clicked' array
       }
    }
    

    然后设置你的样式,它会是这样的:

    backgroundColor: clicked.includes(index) ? "#fff" : "#0e2043",
    

    其中indexmap 回调的第二个参数,如果有的话,最好还是使用项目的id

    backgroundColor: clicked.includes(item.id) ? "#fff" : "#0e2043",
    

    【讨论】:

    • 据我了解,OP只希望当前卡不同。为此,只存储当前活动的项目就足够了。
    • 是的,如果他们一次需要一个项目,则存储单个 id,或者如果可以选择多个项目,则将 id 存储在一个数组中
    【解决方案3】:

    试试这个:

    import React, { useState } from "react";
    import Head from "next/head";
    import { makeStyles } from "@material-ui/core/styles";
    import Accordion from "@material-ui/core/Accordion";
    import AccordionSummary from "@material-ui/core/AccordionSummary";
    import AccordionDetails from "@material-ui/core/AccordionDetails";
    import Typography from "@material-ui/core/Typography";
    import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
    
    const useStyles = makeStyles((theme) => ({
      root: {
        width: "100%"
      },
      heading: {
        fontSize: theme.typography.pxToRem(15),
        fontWeight: theme.typography.fontWeightRegular
      }
    }));
    
    const CARD_ONE = 1;
    const CARD_TWO = 2;
    const CARD_DEFAULT = -1;
    
    export default function App() {
      const classes = useStyles();
      const [clickedCard, setClickedCard] = useState(CARD_DEFAULT);
      console.log(clickedCard);
    
      return (
        <>
          <Head>
            <meta name="description" content="Generated by create next app" />
            <link rel="icon" href="/favicon.ico" />
            <link
              rel="stylesheet"
              href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"
              integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
              crossorigin="anonymous"
            />
            <link
              rel="stylesheet"
              href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/fontawesome.min.css"
              integrity="sha384-wESLQ85D6gbsF459vf1CiZ2+rr+CsxRY0RpiF1tLlQpDnAgg6rwdsUF1+Ics2bni"
              crossorigin="anonymous"
            />
          </Head>
          <div className={classes.root}>
            <div style={{ minHeight: "100vh", backgroundColor: "#0e2043" }}>
              <div className="container" style={{ paddingTop: "80px" }}>
                <div className="row">
                  <div className="col-md-6 faqpadding">
                    <Accordion
                      className="cardHeight"
                      style={{
                        backgroundColor:
                          clickedCard === CARD_ONE ? "#fff" : "#0e2043",
                        boxShadow: "0px 4px 22px 9px rgba(0, 0, 0, 0.17)",
                        border: "1px solid #919191",
                        color: clickedCard === CARD_ONE ? "#0e2043" : "#E3AB50",
                        padding: "20px"
                      }}
                    >
                      <AccordionSummary
                        expandIcon={<ExpandMoreIcon />}
                        aria-controls="panel1a-content"
                        id="panel1a-header"
                        onClick={() => setClickedCard(CARD_ONE)}
                      >
                        <Typography
                          className={classes.heading}
                          style={{ textAlign: "center" }}
                        >
                          WHAT IS CITIZENSHIP-BY-INVESTMENT?
                        </Typography>
                      </AccordionSummary>
                      <AccordionDetails>
                        <Typography>
                          Citizenship-by-Investment (“CBI”) schemes offers instant
                          passport to foreigners to make a a lump sum donation to
                          the state or through buying a qualified real estate. The
                          concept of CBI was first born in St Kitts and Nevis in
                          1984.
                        </Typography>
                      </AccordionDetails>
                    </Accordion>
                  </div>
                  <div className="col-md-6 faqpadding">
                    <Accordion
                      className="cardHeight"
                      style={{
                        backgroundColor:
                          clickedCard === CARD_TWO ? "#fff" : "#0e2043",
                        boxShadow: "0px 4px 22px 9px rgba(0, 0, 0, 0.17)",
                        border: "1px solid #919191",
                        color: clickedCard === CARD_TWO ? "#0e2043" : "#E3AB50",
                        padding: "20px"
                      }}
                    >
                      <AccordionSummary
                        expandIcon={<ExpandMoreIcon />}
                        aria-controls="panel1a-content"
                        id="panel1a-header"
                        onClick={() => setClickedCard(CARD_TWO)}
                      >
                        <Typography
                          className={classes.heading}
                          style={{ textAlign: "center" }}
                        >
                          WHICH IS THE PROGRAM THAT GETS ME INTO MOST COUNTRIES?
                        </Typography>
                      </AccordionSummary>
                      <AccordionDetails>
                        <Typography>
                          Citizenship-by-Investment (“CBI”) schemes offers instant
                          passport to foreigners to make a a lump sum donation to
                          the state or through buying a qualified real estate. The
                          concept of CBI was first born in St Kitts and Nevis in
                          1984.
                        </Typography>
                      </AccordionDetails>
                    </Accordion>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </>
      );
    }
    
    

    Codesandbox Link

    【讨论】:

      猜你喜欢
      • 2021-08-23
      • 1970-01-01
      • 2013-09-23
      • 2020-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-25
      • 1970-01-01
      相关资源
      最近更新 更多