Home > Net >  Why does this code work until the page is refreshed?
Why does this code work until the page is refreshed?

Time:12-27

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.

  • Related