"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;
谢谢:)
为了使上面的代码按您预期的方式工作,需要进行的主要更改是:
仅当该标志为真时,才将每个 Effect 中的守卫添加到 运行。 maleIsChecked 示例:
if(!maleIsChecked) { return; }
对于同时显示两者的效果,请确保仅当两个标志都关闭时才运行:
if(femaleIsChecked || maleIsChecked) { return;}
并且它 运行 每次更改
useEffect(() => { ... }, [chartData, maleIsChecked, femaleIsChecked]):
更新切换功能以“正确”切换。 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;
我在 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;
谢谢:)
为了使上面的代码按您预期的方式工作,需要进行的主要更改是:
仅当该标志为真时,才将每个 Effect 中的守卫添加到 运行。 maleIsChecked 示例:
if(!maleIsChecked) { return; }
对于同时显示两者的效果,请确保仅当两个标志都关闭时才运行:
if(femaleIsChecked || maleIsChecked) { return;}
并且它 运行 每次更改
useEffect(() => { ... }, [chartData, maleIsChecked, femaleIsChecked]):
更新切换功能以“正确”切换。 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;