React中子组件向祖父组件传递事件的优化方案咨询
解决多层回调繁琐的几种方案
1. 组件组合(Component Composition)
这种方式让父组件直接控制子组件的渲染逻辑,跳过中间组件的props转发,保持中间组件的纯展示性。
修改ProductList,让它接收一个渲染函数作为children,只负责渲染列表容器和循环产品:
function ProductList({ products, children }) { return ( <> <h1>Product List</h1> <ul> {products.map(product => children(product))} </ul> </> ); }
在App中直接传入带删除回调的ProductItemRow:
function App() { const [products, setProducts] = useState([]); // 原有fetchProducts、deleteProductItem、useEffect逻辑不变... return ( <> <ProductList products={products}> {(product) => ( <ProductItemRow key={product.id} product={product} onDelete={() => deleteProductItem(product)} /> )} </ProductList> </> ); }
ProductItemRow仅接收必要的展示数据和回调:
function ProductItemRow({ product, onDelete }) { return ( <li> {product.title} <button onClick={onDelete}>Delete</button> </li> ); }
ProductList完全不用关心删除逻辑,复用性不受任何影响。
2. 局部Context
不用全局状态,创建局部作用域的Context,仅在需要的组件树中提供上下文,既避免多层props传递,又不会污染全局状态影响组件复用。
先创建Context和简化消费的自定义Hook:
import { createContext, useContext } from 'react'; const ProductActionsContext = createContext(null); export function useProductActions() { const context = useContext(ProductActionsContext); if (!context) { throw new Error('useProductActions must be used within a ProductActionsProvider'); } return context; }
在App中用Provider包裹ProductList,提供删除函数:
function App() { const [products, setProducts] = useState([]); // 原有fetchProducts、deleteProductItem、useEffect逻辑不变... return ( <ProductActionsContext.Provider value={{ deleteProductItem }}> <ProductList products={products} /> </ProductActionsContext.Provider> ); }
修改ProductItemRow,直接通过Hook获取删除函数:
function ProductItemRow({ product }) { const { deleteProductItem } = useProductActions(); return ( <li> {product.title} <button onClick={() => deleteProductItem(product)}>Delete</button> </li> ); }
ProductList无需做任何修改,保持纯展示特性。这个Context仅在当前组件树生效,其他地方复用ProductList时,只需重新提供对应的Provider即可。
3. 自定义Hook封装业务逻辑
把数据获取、删除等状态逻辑抽离到自定义Hook中,组件只负责渲染视图,分离逻辑与UI,同时保证组件复用性。
创建自定义Hook:
import { useState, useEffect } from 'react'; export function useProducts() { const [products, setProducts] = useState([]); const fetchProducts = async () => { const response = await fetch('https://api.escuelajs.co/api/v1/products'); const data = await response.json(); setProducts(data); }; const deleteProductItem = (productToDelete) => { setProducts(prev => prev.filter(product => product.id !== productToDelete.id)); console.log('Deleted:', productToDelete); // 这里可添加后端API调用等逻辑 }; useEffect(() => { fetchProducts(); }, []); return { products, deleteProductItem }; }
在App中使用Hook,再结合局部Context传递逻辑:
function App() { const { products, deleteProductItem } = useProducts(); return ( <ProductActionsContext.Provider value={{ deleteProductItem }}> <ProductList products={products} /> </ProductActionsContext.Provider> ); }
这种方式让组件彻底脱离业务逻辑,专注于渲染,useProducts Hook也能在其他组件中复用。
内容的提问来源于stack exchange,提问作者user19037628
相关产品推荐
相关产品推荐

