除了将状态通过Props向上传递给父组件,是否有更优的组件状态共享方案?
Great question! The state lifting approach you already know is totally valid (and actually React's recommended pattern for shared state between components), but there are definitely more optimized options depending on your specific use case. Let's break them down:
Option 1: React Context API
If your two components are nested deep in the component tree (or you expect more components to need this state later), Context avoids the hassle of prop drilling (passing props through multiple intermediate components that don't need them).
Here's a clean implementation:
import { createContext, useContext, useState, useMemo, useEffect } from 'react'; // Create a dedicated context for your shared state const SharedStateContext = createContext(); // Wrap your components in a Provider (can be App or a closer parent) function App() { const [sharedState, setSharedState] = useState('initial-value'); // Use useMemo to prevent unnecessary re-renders of context consumers const contextValue = useMemo(() => ({ sharedState, setSharedState }), [sharedState]); return ( <SharedStateContext.Provider value={contextValue}> <ComponentA /> <ComponentB /> </SharedStateContext.Provider> ); } // Component A: Updates the shared state function ComponentA() { const { setSharedState } = useContext(SharedStateContext); return ( <button onClick={() => setSharedState('updated-value-from-A')}> Update Shared State </button> ); } // Component B: Uses the state as an useEffect dependency function ComponentB() { const { sharedState } = useContext(SharedStateContext); useEffect(() => { // Run your target logic whenever sharedState changes console.log('Shared state updated:', sharedState); }, [sharedState]); return <div>Current Shared State: {sharedState}</div>; }
Option 2: Lightweight State Management Libraries (Zustand/Jotai)
If you're dealing with state that might be used across multiple parts of your app, or you want to avoid Context's potential re-render bottlenecks, lightweight libraries like Zustand are perfect. They're simpler than Redux but still handle global state with minimal boilerplate.
Example with Zustand:
import { create } from 'zustand'; // Create a global store for your shared state const useSharedStore = create((set) => ({ sharedState: 'initial-value', setSharedState: (newValue) => set({ sharedState: newValue }) })); // Component A: Updates the store function ComponentA() { const setSharedState = useSharedStore(state => state.setSharedState); return ( <button onClick={() => setSharedState('updated-value-from-A')}> Update State </button> ); } // Component B: Consumes the store in useEffect function ComponentB() { const sharedState = useSharedStore(state => state.sharedState); useEffect(() => { console.log('State changed:', sharedState); // Execute your logic here }, [sharedState]); return <div>{sharedState}</div>; }
Option 3: Pub/Sub Pattern (e.g., mitt)
For isolated, one-off component communication where you don't want to add Context or state management overhead, a pub/sub library like mitt works great. It lets components emit and listen to events without needing a direct parent-child relationship.
Example with mitt:
import mitt from 'mitt'; // Create a single emitter instance for your app const emitter = mitt(); // Component A: Emits an event when its local state changes function ComponentA() { const [localState, setLocalState] = useState('initial-value'); useEffect(() => { // Broadcast the updated state to any listeners emitter.emit('state-updated', localState); }, [localState]); return ( <button onClick={() => setLocalState('updated-value-from-A')}> Update Local State </button> ); } // Component B: Listens for the event and reacts function ComponentB() { useEffect(() => { // Define the handler for state updates const handleStateUpdate = (newState) => { console.log('Received new state:', newState); // Run your target logic here }; // Subscribe to the event emitter.on('state-updated', handleStateUpdate); // Clean up the listener to avoid memory leaks return () => { emitter.off('state-updated', handleStateUpdate); }; }, []); return <div>Waiting for state updates...</div>; }
Which Should You Choose?
- State Lifting: Stick with this if your components are direct siblings or close in the tree, and the state is only used by these two components. It's simple, no extra dependencies, and follows React's core patterns.
- Context API: Use this when you have prop drilling issues, or multiple components across the tree need access to the state.
- State Management Library: Ideal for global state, complex state logic, or when you want fine-grained control over re-renders.
- Pub/Sub: Best for isolated, one-off component communication where you don't want to tie components to a parent or global state.
内容的提问来源于stack exchange,提问作者Michael Rýdl

