You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React页面刷新后报错:无法读取null的reduce属性问题求助

问题:页面刷新后reduce调用报错,无法计算金额总和

当前代码首次运行正常,但页面刷新后抛出错误:Uncaught TypeError: Cannot read properties of null (reading 'reduce'),报错位置在Balance.js第27行。核心需求是计算fetch返回数组中所有元素amount字段的总和并在组件中展示。

相关代码

Balance.js

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>
  )
};

解决方案

1. 修正初始状态类型

报错根源是组件初始化时balances为null,刷新后fetch请求完成前,null调用reduce会触发类型错误。将BalanceContext中的初始状态改为空数组:

// BalanceContext.js中修改初始化部分
const [state, dispatch] = useReducer(balancesReducer, {
  balances: [] // 替换原来的null
})

空数组调用reduce会正常返回初始值0,不会报错。

2. 可选:添加加载状态处理

如果需要在数据加载完成前显示友好提示,可在Balance组件中增加判断:

function Balance(){
  const {balances, dispatch} = useBalancesContext()
  const {user} = useAuthContext()
  
  useEffect(() => {
    // 原有代码不变
  }, [dispatch, user]);

  // 数据未加载完成时显示默认值或加载提示
  if (balances.length === 0) {
    return <div className="header"><strong>Available Balance: $0</strong></div>
  }

  const newBalance = balances.reduce((accumulator, currentValue) => accumulator + currentValue.amount, 0)
  return (
    <div className="header">
      <strong>Available Balance: ${newBalance}</strong>
    </div>
  )
}

3. 优化总和计算性能(可选)

使用useMemo缓存计算结果,避免组件每次渲染都重复执行reduce:

import { useEffect, useMemo } from "react";

// 其他代码不变
const newBalance = useMemo(() => {
  return balances.reduce((accumulator, currentValue) => accumulator + currentValue.amount, 0)
}, [balances]) // 仅当balances变化时重新计算

关于reducer计算总和的思路说明

不建议在reducer中用总和替换原始的balances数组,这会丢失原始数据,导致后续无法基于原数组做明细展示、删除等操作。总和属于衍生数据,直接在组件中基于原始数组计算是更合理的方案。

内容的提问来源于stack exchange,提问作者Michael Presson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 06:50:54