I apologize for the ignorance on my part but I can't understand why this code works until the page is refreshed. The main goal is to utilize the array.reduce method on the response.json from my fetch so that I can have a rolling sum of all of the json[].amount element displayed in my component. This code works until the page is refreshed and then it says: Uncaught TypeError: Cannot read properties of null (reading 'reduce') at Balance (Balance.js:27:1). I know at the very least I'm doing something wrong with the state.
Balance.js component:
import { useEffect } from "react";
import {useBalancesContext} from '../hooks/useBalancesContext'
import { useAuthContext } from '../hooks/useAuthContext'
function Balance(){
const {balances, dispatch} = useBalancesContext()
const {user} = useAuthContext()
useEffect(() => {
const addBalances = async () => {
const response = await fetch("http://localhost:4000/api/balances", {
headers: {
'Authorization': `Bearer ${user.token}`
}
});
const json = await response.json();
if (response.ok) {
dispatch({type: 'SET_BALANCES', payload: json})
}
}
if (user) {
addBalances();
}
}, [dispatch, user]);
const newBalance = balances.reduce((accumulator, currentValue) => accumulator currentValue.amount, 0)
return (
<div className="header">
<strong>Available Balance: ${newBalance}</strong>
</div>
)
}
export default Balance;
BalanceContext.js:
import { createContext, useReducer } from "react";
export const BalancesContext = createContext();
export const balancesReducer = (state, action) => {
switch (action.type) {
case 'SET_BALANCES':
return {
balances: action.payload
}
/* case 'SUM_BALANCES':
return {
balances: state.balances.amount.reduce((accumulator, currentValue) => accumulator currentValue.amount, 0)
} */
case 'CREATE_BALANCE':
return {
balances: [action.payload, ...state.balances]
}
case 'DELETE_BALANCE':
return {
balances: state.balances.filter((b) => b._id !== action.payload._id)
}
default:
return state
}
}
export const BalancesContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(balancesReducer, {
balances: null
})
return (
<BalancesContext.Provider value={{...state, dispatch}}>
{children}
</BalancesContext.Provider>
)
};
I've tried using the array.reduce method inside of the function but then I don't have access to the newBalance value inside of Balance component. I've also tried using array.reduce in the reducer as well but I'm not confident I was on the right track with that either.
CodePudding user response:
balances
is initialially null
, so its value may not be set when the page is loaded.
A good approach with values calculated with .reduce()
is to use the useMemo
hook:
import { useMemo } from 'react';
const newBalance = useMemo(() => {
if (!balances) return 0;
return balances.reduce(
(accumulator, currentValue) => accumulator currentValue.amount,
0
);
}, [balances]);
This will tell React to calculate newBalance
only when balances
changes and will return 0
if balances
is not already set.