【问题标题】:Bar chart is not being updated when "untick" the checkboxes in React Hooks“取消选中” React Hooks 中的复选框时,条形图未更新
【发布时间】:2021-12-31 15:43:09
【问题描述】:

我在反应应用程序中创建了一个条形图。此图表是动态的,通过选中每个复选框,需要上传一组数据。

问题:数据正在更新并显示在图表上,但是当我单击该特定复选框时,没有任何反应,并且图表没有恢复到原始状态。我尝试切换复选框的状态,但问题仍然存在并且图表没有更新。

这是我的代码:

import React, { useEffect, useState } from "react";
import { Bar } from "react-chartjs-2";

const RandomStatic = (props) => {
  let jsonData = [
    { gender: "female" },
    { gender: "male" },
    { gender: "female" },
    { gender: "male" },
    { gender: "female" },
    { gender: "male" },
    { gender: "female" },
    { gender: "female" },
    { gender: "female" },
    { gender: "female" },
  ];

  const [userFemaleCounter, setUserFemaleCounter] = useState();

  const [userMaleCounter, setUserMaleCounter] = useState();

  const [totalCounter, setTotalCounter] = useState();

  const [femalePercentage, setFemalePercentage] = useState();

  const [malePercentage, setMalePercentage] = useState();

  const [chartData, setChartData] = useState(jsonData);

  const [femaleIsChecked, setFemaleIsChecked] = useState(false);

  const [maleIsChecked, setMaleIsChecked] = useState(false);



 useEffect(() => {

    // Getting all Data in an array
    let allData = jsonData.map(function (e) {

      return e.gender;

    });
console.log(allData);
// Display Gender data on the chart
let fCounter = [];
let mCounter = [];
allData.forEach((gender) => {
  if (gender === "female") {
    fCounter = ++fCounter;
  } else if (gender === "male") {
    mCounter = ++mCounter;
  }
});


let userFemaleCounter = fCounter;
setUserFemaleCounter(fCounter);
console.log(userFemaleCounter);

let userMaleCounter = mCounter;
setUserMaleCounter(mCounter);
console.log(userMaleCounter);

let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);
console.log(totalCounter);

let femalePercentage = (fCounter / totalCounter) * 100;
setFemalePercentage(femalePercentage);

let malePercentage = (mCounter / totalCounter) * 100;
setMalePercentage(malePercentage);


}, [chartData]);

useEffect(() => {
    ///change the jsonData
    //setChartData
    let allData = chartData.map(function (e) {
      return e.gender;
    });
    let mCounter = [];
    let fCounter = [];
    allData.forEach((gender) => {
      if (gender === "female") {
        fCounter = ++fCounter;
      } else if (gender === "male") {
        mCounter = ++mCounter;
      }
    });

 let totalCounter = fCounter + mCounter;
    setTotalCounter(totalCounter);

let userFemaleCounter = fCounter;
setUserFemaleCounter(userFemaleCounter);
let femalePercentage = (fCounter / totalCounter) * 100;
setFemalePercentage(femalePercentage);
let userMaleCounter = 0;
setUserMaleCounter(userMaleCounter);
let malePercentage = 0;
setMalePercentage(malePercentage);



}, [femaleIsChecked]);




//useeffect for MALE checkbox
  useEffect(() => {



let allData = jsonData.map(function (e) {
  return e.gender;
});
let mCounter = [];
let fCounter = [];
allData.forEach((gender) => {
  if (gender === "female") {
    fCounter = ++fCounter;
  } else if (gender === "male") {
    mCounter = ++mCounter;
  }
});

let totalCounter = fCounter + mCounter;
setTotalCounter(totalCounter);

let malePercentage = (mCounter / totalCounter) * 100;
setMalePercentage(malePercentage);

fCounter = [];
let femalePercentage = [];
setFemalePercentage(femalePercentage);


}, [maleIsChecked]);





const toggleFemale = () => {
  
    if(femaleIsChecked == true)
    {
      setFemaleIsChecked(false)
    }else{
      setFemaleIsChecked(true)
    }
    
  }


 const toggleMale = () => setMaleIsChecked(chartData)
  const toggleAll = () => setChartData(!chartData)
  //console.log(toggleFemale)



return (
    <div>
      <Bar
        className="chart"
        data={{
          labels: ["Female", "Male"],
          datasets: [
            {
              data: [femalePercentage, malePercentage],
              backgroundColor: ["green", "yellow"],
              borderColor: ["green", "yellow"],
              borderWidth: 0.5,
            },
          ],
        }}
    options={{
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        title: {
          display: true,
          text: "Male and Female Ratio",
        },
        legend: {
          display: false,
        },
      },
      scales: {
        y: {
          display: true,
          title: {
            display: true,
            text: "Percentage",
          },
        },
        x: {
          display: true,
          title: {
            display: true,
            text: "Population",
          },
        },
      },
      showTooltips: false,
      hover: false,
    }}
  />
  <div className="checkbox-form-wrapper">
    <div className="chartBox">
      <label>Female Only</label>
      <input
        name="femalePercentage"
        type="checkbox"
        //checked={femaleIsChecked}
        onChange={toggleFemale}
      />
      <label>Male Only</label>
      <input
        name="malePercentage"
        type="checkbox"
        checked={maleIsChecked}
        onChange={toggleMale}
        
      />
      <label>Both Female and Male</label>
      <input
        name="chartData"
        type="checkbox"
        checked={chartData}
        onChange={toggleAll}
      />
    </div>
  </div>
</div>


 );
};

export default RandomStatic;

谢谢你:)

【问题讨论】:

    标签: javascript reactjs api react-hooks fetch-api


    【解决方案1】:

    为了使上面的代码按预期工作,需要进行的主要更改是:

    1. 在每个 Effect 中添加守卫以仅在该标志为 true 时运行。 ma​​leIsChecked 示例:

      if(!maleIsChecked) { 返回; }

    2. 对于同时显示两者的效果,请确保它仅在两个标志都关闭时运行:

      if(femaleIsChecked || maleIsChecked) { return;}

      并且它会在每次更改时运行

      useEffect(() => { ... }, [chartData, maleIsChecked, femaleIsChecked]):

    3. 更新切换功能以“正确”切换。 toggleMale 示例:

      const toggleMale = () => setMaleIsChecked(!maleIsChecked)

    但是,我强烈建议将方法从复选框切换到单选按钮。对我来说,这似乎比交互更自然,因为你有所有的选项“摆在桌面上”,而且它们是相互排斥的。下面建议的工作代码,用 CHANGE 突出显示的更改:

    import React, { useEffect, useState } from "react";
    import { Bar } from "react-chartjs-2";
    
    // CHANGE: define an enum to handle data types to be displayed
    const TypesOfDataToDisplay = {
        MALE: 'male',
        FEMALE: 'female',
        BOTH: 'both'
    }
    
    const RandomStatic = (props) => {
      let jsonData = [
        { gender: "female" },
        { gender: "male" },
        { gender: "female" },
        { gender: "male" },
        { gender: "female" },
        { gender: "male" },
        { gender: "female" },
        { gender: "female" },
        { gender: "female" },
        { gender: "female" },
      ];
    
      const [userFemaleCounter, setUserFemaleCounter] = useState();
    
      const [userMaleCounter, setUserMaleCounter] = useState();
    
      const [totalCounter, setTotalCounter] = useState();
    
      const [femalePercentage, setFemalePercentage] = useState();
    
      const [malePercentage, setMalePercentage] = useState();
    
      const [chartData, setChartData] = useState(jsonData);
    
      // CHANGE: define a new state that indicates data type to be displayed
      const [dataToDisplay, setDataToDisplay] = useState(TypesOfDataToDisplay.BOTH);
    
      // CHANGE: remove boolean states (using them, for each toggle you will generate two renderings)
      //const [femaleIsChecked, setFemaleIsChecked] = useState(false);
      //const [maleIsChecked, setMaleIsChecked] = useState(false);
    
      useEffect(() => {
    
        // CHANGE: skip this effect if not needed
        if(dataToDisplay !== TypesOfDataToDisplay.BOTH) {
            return;
        }
    
        // Getting all Data in an array
        let allData = jsonData.map(function (e) {
    
          return e.gender;
    
        });
        console.log(allData);
        // Display Gender data on the chart
        let fCounter = [];
        let mCounter = [];
        allData.forEach((gender) => {
          if (gender === "female") {
            fCounter = ++fCounter;
          } else if (gender === "male") {
            mCounter = ++mCounter;
          }
        });
    
    
        let userFemaleCounter = fCounter;
        setUserFemaleCounter(fCounter);
        console.log(userFemaleCounter);
    
        let userMaleCounter = mCounter;
        setUserMaleCounter(mCounter);
        console.log(userMaleCounter);
    
        let totalCounter = fCounter + mCounter;
        setTotalCounter(totalCounter);
        console.log(totalCounter);
    
        let femalePercentage = (fCounter / totalCounter) * 100;
        setFemalePercentage(femalePercentage);
    
        let malePercentage = (mCounter / totalCounter) * 100;
        setMalePercentage(malePercentage);
    
    
      }, [chartData, dataToDisplay]); // CHANGE: make sure the effect is called on data type change
    
      useEffect(() => {
    
        // CHANGE: skip this effect if not needed
        if(dataToDisplay !== TypesOfDataToDisplay.FEMALE) {
            return;
        }
    
        ///change the jsonData
        //setChartData
        let allData = chartData.map(function (e) {
          return e.gender;
        });
        let mCounter = [];
        let fCounter = [];
        allData.forEach((gender) => {
          if (gender === "female") {
            fCounter = ++fCounter;
          } else if (gender === "male") {
            mCounter = ++mCounter;
          }
        });
    
        let totalCounter = fCounter + mCounter;
        setTotalCounter(totalCounter);
    
        let userFemaleCounter = fCounter;
        setUserFemaleCounter(userFemaleCounter);
        let femalePercentage = (fCounter / totalCounter) * 100;
        setFemalePercentage(femalePercentage);
        let userMaleCounter = 0;
        setUserMaleCounter(userMaleCounter);
        let malePercentage = 0;
        setMalePercentage(malePercentage);
    
    
    
      }, [dataToDisplay]); // CHANGE: make sure the effect is called on data type change
    
    
    
    
      //useeffect for MALE checkbox
      useEffect(() => {
    
        // CHANGE: skip this effect if not needed
        if(dataToDisplay !== TypesOfDataToDisplay.MALE) {
            return;
        }
    
        let allData = jsonData.map(function (e) {
          return e.gender;
        });
        let mCounter = [];
        let fCounter = [];
        allData.forEach((gender) => {
          if (gender === "female") {
            fCounter = ++fCounter;
          } else if (gender === "male") {
            mCounter = ++mCounter;
          }
        });
    
        let totalCounter = fCounter + mCounter;
        setTotalCounter(totalCounter);
    
        let malePercentage = (mCounter / totalCounter) * 100;
        setMalePercentage(malePercentage);
    
        fCounter = [];
        let femalePercentage = [];
        setFemalePercentage(femalePercentage);
    
    
      }, [dataToDisplay]); // CHANGE: make sure the effect is called on data type change
    
      // CHANGE: remove togglers for the boolean states
      /*const toggleFemale = () => {
      
        if(femaleIsChecked == true)
        {
          setFemaleIsChecked(false)
        }else{
          setFemaleIsChecked(true)
        }
        
      }
    
    
     const toggleMale = () => setMaleIsChecked(chartData)
      const toggleAll = () => setChartData(!chartData)*/
    
    // CHANGE: add only one handler for changing data to be displayed
    const toggleDisplay = (event) => {
        setDataToDisplay(event.currentTarget.value);
    }
    
        // CHANGE: switch from checkboxes to radio buttons and update definitions with new handler
      return (
        <div className="chartContainer">
          <Bar
            className="chart"
            data={{
              labels: ["Female", "Male"],
              datasets: [
                {
                  data: [femalePercentage, malePercentage],
                  backgroundColor: ["green", "yellow"],
                  borderColor: ["green", "yellow"],
                  borderWidth: 0.5,
                },
              ],
            }}
            options={{
              responsive: true,
              maintainAspectRatio: false,
              plugins: {
                title: {
                  display: true,
                  text: "Male and Female Ratio",
                },
                legend: {
                  display: false,
                },
              },
              scales: {
                y: {
                  display: true,
                  title: {
                    display: true,
                    text: "Percentage",
                  },
                },
                x: {
                  display: true,
                  title: {
                    display: true,
                    text: "Population",
                  },
                },
              },
              showTooltips: false,
              hover: false,
            }}
          />
          <div className="checkbox-form-wrapper">
            <div className="chartBox">
              <label>Female Only</label>
              <input
                name="femalePercentage"
                type="radio"
                value={TypesOfDataToDisplay.FEMALE}
                checked={dataToDisplay === TypesOfDataToDisplay.FEMALE}
                onChange={toggleDisplay}
              />
              <label>Male Only</label>
              <input
                name="malePercentage"
                type="radio"
                value={TypesOfDataToDisplay.MALE}
                checked={dataToDisplay === TypesOfDataToDisplay.MALE}
                onChange={toggleDisplay}
    
              />
              <label>Both Female and Male</label>
              <input
                name="chartData"
                type="radio"
                value={TypesOfDataToDisplay.BOTH}
                checked={dataToDisplay === TypesOfDataToDisplay.BOTH}
                onChange={toggleDisplay}
              />
            </div>
          </div>
        </div>
    
    
      );
    };
    
    export default RandomStatic;

    【讨论】:

      猜你喜欢
      • 2020-08-12
      • 1970-01-01
      • 2021-10-23
      • 1970-01-01
      • 1970-01-01
      • 2019-01-14
      • 1970-01-01
      • 2019-02-02
      • 2021-10-06
      相关资源
      最近更新 更多