React/recompose组件props变更时如何触发更新?
我一眼就看出问题大概率出在React的状态更新机制和你的购物车数据类的结合方式上,而非withPropsOnChange本身的问题。咱们一步步拆解:
核心原因:React组件的props更新依赖父组件的重新渲染
withPropsOnChange的触发逻辑是:当监听的props发生浅比较可感知的变化时,才会执行回调并更新派生props。但这里有个前提——父组件必须因为状态更新而重新渲染,把新的props传递给子组件。
你提到currentProducts和setProducts是自定义购物车类的属性和方法,这就麻烦了:如果这个类的属性变化没有触发父组件的React状态更新,那么父组件根本不会重新渲染,子组件拿到的currentProductsprops还是旧的引用(即使类里的数组内容变了),withPropsOnChange自然不会执行。
哪怕你后来改成用concat生成新数组,只要setProducts只是修改了类的属性,没有触发父组件的state更新,父组件就不会把新的数组传递给子组件,withPropsOnChange的监听条件依然不满足。
具体解决方案
方案1:把购物车数据放到父组件的React State中
这是最符合React设计模式的做法,直接利用React的状态管理机制触发更新:
// 父组件示例 import { useState } from 'react'; import Product from './Product'; import { addToCart } from 'utils/cart'; function ProductList() { // 把currentProducts作为React状态管理 const [currentProducts, setCurrentProducts] = useState([]); const handleSetProducts = (newProducts) => { setCurrentProducts(newProducts); }; return ( <div> {products.map(product => ( <Product key={product.id} product={product} currentProducts={currentProducts} setProducts={handleSetProducts} /> ))} </div> ); }
然后在你的Product组件的addToCart handler中,更新状态时要创建新数组(避免修改原数组):
addToCart: ({ product, setProducts, currentProducts }) => () => { addToCart(product.id).then((success) => { if (success) { // 用扩展运算符创建新数组,确保引用变化 setProducts([...currentProducts, product.id]); } }); }
这样一来,currentProducts状态更新后,父组件会重新渲染,子组件拿到新的props,withPropsOnChange就会正常触发,计算addedToCart的值。
方案2:给自定义购物车类添加订阅机制(如果你坚持用类管理数据)
如果不想把购物车数据放到组件state里,可以让你的购物车类支持订阅/发布模式,当数据变化时通知父组件更新state:
// 购物车类示例 class CartModel { constructor() { this.currentProducts = []; this.subscribers = []; } // 订阅数据变化 subscribe(callback) { this.subscribers.push(callback); // 返回取消订阅的方法 return () => { this.subscribers = this.subscribers.filter(cb => cb !== callback); }; } setProducts(products) { this.currentProducts = products; // 通知所有订阅者数据更新 this.subscribers.forEach(callback => callback([...this.currentProducts])); } // 其他方法... } // 全局实例或者通过上下文传递 export const cartModel = new CartModel();
然后在父组件中订阅数据变化,更新组件state:
// 父组件 import { useState, useEffect } from 'react'; import { cartModel } from './cartModel'; function ProductList() { const [currentProducts, setCurrentProducts] = useState(cartModel.currentProducts); useEffect(() => { // 订阅购物车数据变化,更新组件state const unsubscribe = cartModel.subscribe(newProducts => { setCurrentProducts(newProducts); }); // 组件卸载时取消订阅 return unsubscribe; }, []); return ( <div> {products.map(product => ( <Product key={product.id} product={product} currentProducts={currentProducts} setProducts={cartModel.setProducts.bind(cartModel)} /> ))} </div> ); }
这样购物车类的数据变化会触发父组件的state更新,进而让子组件拿到新的props,withPropsOnChange就能正常工作了。
快速验证方法
你可以先在父组件中打印currentProducts,并观察当添加商品后父组件有没有重新渲染。如果父组件没重新渲染,那肯定是状态更新的问题,和withPropsOnChange无关。
内容的提问来源于stack exchange,提问作者Gustavo Straube

