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

React Native Redux状态变更后组件未触发重渲染问题

Redux状态更新后React Native UI不自动同步重渲染

问题描述

我在React Native中开发购物篮组件,购物篮内容和总价存储在全局Redux store中。用户选择商品时会派发action,将商品加入购物篮并更新总价。
但全局状态变更后,UI没有同步更新,只有修改本地状态强制组件重渲染时,才会正确显示store里的最新值——Reducer里打印basketPrice每次都能拿到正确的更新结果。


问题相关代码

Reducer代码

const INITIAL_STATE = {
basket: [],
basketPrice: 0,
};

const mainReducer = (state = INITIAL_STATE, action) => {
    const stateCpy = state;
    switch (action.type) {
        case 'SET_BASKET':
            stateCpy.basket = action.payload
            return stateCpy;
        case 'ADD_TO_BASKET':
            stateCpy.basket.push(action.payload)
            return stateCpy
        case 'REMOVE_FROM_BASKET':
            let tempItems = stateCpy.basket
            for (var x = 0; x < stateCpy.basket.length; x++) {
                if (stateCpy.basket[x]._id === action.payload) {
                    tempItems.splice(x, 1)
                    break;
                }
            }
            stateCpy.basket = tempItems
            return stateCpy
        case 'SET_BASKET_PRICE':
            stateCpy.basketPrice = action.payload
            console.log(stateCpy.basketPrice)
            return stateCpy
        default:
            return state
    }
};

export default mainReducer

Action代码

const setBasket = basket => ({
    type: 'SET_BASKET',
    payload: basket,
});
const addToBasket = item => ({
    type: 'ADD_TO_BASKET',
    payload: item,
});
const removeFromBasket = item_id => ({
    type: 'REMOVE_FROM_BASKET',
    payload: item_id,
});

const setBasketPrice = price => ({
    type: 'SET_BASKET_PRICE',
    payload: price,
});

export default actions = {
    setBasket,
    addToBasket,
    removeFromBasket,
    setBasketPrice
}

UI组件代码

... 

import { useSelector, useDispatch } from 'react-redux'

export const RestaurantView = ({ navigation, route }) => {
    const basket = useSelector((state) => state.basket)
    const basketPrice = useSelector((state) => state.basketPrice)
    
    const dispatch = useDispatch()

...

function calcBasketPrice(){
        let tempBasketPrice = 0
        basket.forEach(element => {
            tempBasketPrice += element.price
        });
        return tempBasketPrice
        
    }

    function addToBasket(item) {
        dispatch(actions.setBasketPrice(calcBasketPrice() + item.price))
        dispatch(actions.addToBasket(item))
    }


return ( <View>
    <ItemCard onPress={addToBasket}> </ItemCard>
    <Text style={{ textAlign: "right", padding: 15, fontSize: 20 }}> {basketPrice}</Text>
</View>)

}

问题原因

核心问题是违反了Redux的不可变更新原则。
Redux判断状态是否更新、是否通知订阅组件重渲染的依据是:reducer返回的状态对象引用是否发生变化。你的代码所有更新操作都是直接修改原state对象,没有生成新的状态引用:

  • const stateCpy = state 仅拷贝了原state的引用,没有创建真正的状态副本,后续对stateCpy的修改本质都是直接修改原state
  • 对basket数组使用push、splice方法都是直接修改原数组,后续重新赋值也只是传递同一个数组的引用,没有生成新数组
  • 直接修改stateCpy.basketPrice属于修改原对象属性,最终返回的stateCpy和传入的原state是同一个引用,Redux检测到引用无变化,就不会触发UI组件重渲染。这就是reducer内能打印到正确值,但UI不更新的根本原因。

除此之外代码还有两个逻辑缺陷:

  • 总价计算放在组件中,分两次dispatch分别更新价格和购物篮内容,存在状态不一致的风险
  • 删除商品的逻辑直接修改原数组,同样属于可变更新,会导致后续其他状态更新异常

修复方案

1. 重写Reducer,所有更新都返回新的状态对象,禁止直接修改原state

const INITIAL_STATE = {
  basket: [],
  basketPrice: 0,
};

const mainReducer = (state = INITIAL_STATE, action) => {
  switch (action.type) {
    case 'SET_BASKET':
      return {
        ...state,
        basket: [...action.payload]
      };
    case 'ADD_TO_BASKET':
      // 生成新数组,总价统一在reducer内计算,不需要单独dispatch
      const newBasketAdd = [...state.basket, action.payload]
      return {
        ...state,
        basket: newBasketAdd,
        basketPrice: newBasketAdd.reduce((total, item) => total + item.price, 0)
      };
    case 'REMOVE_FROM_BASKET':
      // filter返回新数组,不修改原数组
      const newBasketRemove = state.basket.filter(item => item._id !== action.payload)
      return {
        ...state,
        basket: newBasketRemove,
        basketPrice: newBasketRemove.reduce((total, item) => total + item.price, 0)
      };
    case 'SET_BASKET_PRICE':
      return {
        ...state,
        basketPrice: action.payload
      };
    default:
      return state
  }
};

export default mainReducer

2. 简化组件加购逻辑,无需单独计算价格派发action

function addToBasket(item) {
  // 仅派发加购动作,reducer统一处理数组更新和价格计算,保证状态一致性
  dispatch(actions.addToBasket(item))
}

补充:官方推荐的Redux Toolkit内置了Immer能力,允许直接写修改状态的语法,底层会自动生成不可变更新的新状态,可以从根源上避免这类引用不更新的问题。


内容的提问来源于stack exchange,提问作者J Gi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 09:45:38