I'm trying to use React to count a certain attribute and its frequency within an object. I use a map() but the return function always shows the default value 0.
let countRechnung = 0;
React.useEffect(() => {
{
dokumente.length &&
dokumente.map((doc, key) => {
if (doc.einordnung == 1) {
countRechnung ;
} else if (doc.einordnung == 2) {
countWerbung ;
} else if (doc.einordnung == 3) {
countInfo ;
} else {
countSonstiges ;
}
});
}
}, [dokumente]);
[...]
return(
...
<MenuItem value={1}>Rechnungen ({countRechnung}) // It's 0 </MenuItem>
...)
CodePudding user response:
To get this algorithm working you'll need to actually store the counted values. I'd recommend making use of useMemo
instead of useEffect
.
Try this:
const { countRechnung, countWerbung, countInfo, countSonstiges } = useMemo(() => {
const results = {
countRechnung: 0,
countWerbung: 0,
countInfo: 0,
countSonstiges: 0,
}
dokumente.forEach(doc => {
if (doc.einordnung === 1)
results.countRechnung = results.countRechnung 1;
else if (doc.einordnung === 2)
results.countWerbung = results.countWerbung 1;
else if (doc.einordnung === 3)
results.countInfo = results.countInfo 1;
else
results.countSonstiges= results.countSonstiges 1;
});
return results;
}, [dokumente])
Then in your implementation:
return (
...
<MenuItem value={1}>Rechnungen ({countRechnung})</MenuItem>
...
)
No need to use map
since you don't need to transform anything, rather just use forEach
. Also no need to check for dokumente
length.
CodePudding user response:
useEffect
runs the code after rendering is complete. If you want to calculate something that effects the render result, you need to do it in the body of the component. Additionally, .map
's purpose is to create a new array. Since you're not using that array, you should probably use .forEach
instead:
let countRechnung = 0;
dokumente.length &&
dokumente.forEach((doc, key) => {
if (doc.einordnung == 1) {
countRechnung ;
} else if (doc.einordnung == 2) {
countWerbung ;
} else if (doc.einordnung == 3) {
countInfo ;
} else {
countSonstiges ;
}
});
[...]
return(
...
<MenuItem value={1}>Rechnungen ({countRechnung}) // It's 0 </MenuItem>
...)
CodePudding user response:
countRechnung
is redeclared 0
each render cycle and the useEffect
hook runs at the end of the render cycle, so the UI will never see the mutated countRechnung
value.
Use an array reduce to compute the countRechnung
value. The array methods can handle empty arrays, so the array length check is a little unnecessary.
const counts = dokumente.reduce((counts, doc) => {
if (doc.einordnung == 1) {
counts.countRechnung ;
} else if (doc.einordnung == 2) {
counts.countWerbung ;
} else if (doc.einordnung == 3) {
counts.countInfo ;
} else {
counts.countSonstiges ;
}
return counts;
}, {
countRechnung: 0,
countWerbung: 0,
countInfo: 0,
countSonstiges: 0,
});
...
return(
...
<MenuItem value={1}>
Rechnungen ({counts.countRechnung})
</MenuItem>
...
);
I think a switch
state is a little more clean, but this is just my opinion.
const counts = dokumente.reduce((counts, doc) => {
switch(Number(doc.einordnung)) {
case 1:
counts.countRechnung ;
break;
case 2:
counts.countWerbung ;
break;
case 3:
counts.countInfo ;
break;
default:
counts.countSonstiges ;
}
return counts;
}, {
countRechnung: 0,
countWerbung: 0,
countInfo: 0,
countSonstiges: 0,
});
If this counting logic is expensive, memoize the result with the useMemo
hook.
const counts = useMemo(() => {
return dokumente.reduce((counts, doc) => {
switch(Number(doc.einordnung)) {
case 1:
counts.countRechnung ;
break;
case 2:
counts.countWerbung ;
break;
case 3:
counts.countInfo ;
break;
default:
counts.countSonstiges ;
}
return counts;
}, {
countRechnung: 0,
countWerbung: 0,
countInfo: 0,
countSonstiges: 0,
});
}, [dokumente]); // <-- recompute when `dokumente` value updates
CodePudding user response:
With every rerender the variable countRechnung is gonna get reset again. Place it outside the component or (better way) use the useState hook:
// Not sure if this one works: const [countRechnung, setCountRechnung] = React.useState(0);
const [countRechnung, incrementCountRechnung] = React.useReducer((s) => s 1, 0) // s is the value it had before, calling incrementCountWerbung will increase the value by one, if you don't know what the useReducer is look it up
// You should also have a look at the React useState hook. Most of your variables will be set with useState in React
React.useEffect(() => {
{
dokumente.length &&
dokumente.map((doc, key) => {
if (doc.einordnung == 1) {
// if you use the commented out method: (Not sure if it works): setCountWerbung(countWerbung 1)
incrementCountRechnung()
} else if (doc.einordnung == 2) {
incrementCountWerbung()
} else if (doc.einordnung == 3) {
incrementCountInfo()
} else {
incrementSonstiges()
}
});
}
}, [dokumente]);
[...]
return(
...
<MenuItem value={1}>Rechnungen ({countRechnung}) // It's 0 </MenuItem>
...)