"untick" React Hooks 中的复选框未更新条形图

Bar chart is not being updated when "untick" the checkboxes in React Hooks

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

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

这是我的代码:

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;

谢谢:)

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

  1. 仅当该标志为真时,才将每个 Effect 中的守卫添加到 运行。 maleIsChecked 示例:

    if(!maleIsChecked) { return; }

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

    if(femaleIsChecked || maleIsChecked) { return;}

    并且它 运行 每次更改

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

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

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

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

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;