Home > front end >  Increment variable using react and map function
Increment variable using react and map function

Time:04-11

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>
 ...)
  • Related