Next.js中替代Props传递组件状态的方案:如何直接在子组件中调用_app.js内的setToastOpen方法
Absolutely! Prop drilling through 4 levels of components is definitely a pain — React Context is the ideal tool here. It lets you share state updaters like setToastOpen across your entire component tree without manually passing props down every layer. Here's how to implement it:
Step 1: Create a Toast Context
First, create a dedicated context to hold your setToastOpen function. This keeps your state logic organized and reusable.
// contexts/ToastContext.js import { createContext, useContext } from 'react'; // Initialize context with null (we'll provide the actual value in _app.js) const ToastContext = createContext(null); // Export the provider component export const ToastProvider = ToastContext.Provider; // Create a custom hook to simplify accessing the context export const useToast = () => { const context = useContext(ToastContext); // Add a guard clause to catch misuse (optional but helpful) if (!context) { throw new Error('useToast must be used within a ToastProvider'); } return context; };
Step 2: Wrap Your App with the Provider
Update your _app.js to wrap your entire app (including the ToastMessage and page components) with the ToastProvider, passing setToastOpen as the context value. You can remove that useEffect since we no longer need to inject it into pageProps.
// _app.js import { ToastProvider } from '../contexts/ToastContext'; import ToastMessage from '../components/ToastMessage'; function MyApp({ Component, pageProps }) { const [toastOpen, setToastOpen] = React.useState({ msg: '', open: false }); return ( <React.Fragment> <ToastProvider value={setToastOpen}> <ToastMessage message={toastOpen.msg} setOpenState={setToastOpen} openState={toastOpen.open} /> <Component {...pageProps} /> </ToastProvider> </React.Fragment> ); } export default MyApp;
Step 3: Use the Context in Any Child Component
Now, in any component (regardless of how deep it is in the tree), you can import the useToast hook and directly access setToastOpen without any prop drilling:
// components/YourDeepChildComponent.js import { useToast } from '../contexts/ToastContext'; const YourDeepChildComponent = () => { const setToastOpen = useToast(); const triggerToast = () => { // Call setToastOpen with your desired message and open state setToastOpen({ msg: 'This toast came from a deep child component!', open: true }); }; return ( <button onClick={triggerToast}>Show Toast</button> ); }; export default YourDeepChildComponent;
Why This Works
- No more prop drilling: You don't have to pass
setToastOpenthroughpages,layouts, orsections— any component under theToastProvidercan access it directly. - Clean and scalable: If you later need to add more toast-related logic (like auto-closing, different toast types), you can extend the context to include those functions without changing every child component.
- Type-safe (optional): If you're using TypeScript, you can add types to the context to ensure you're using
setToastOpencorrectly.
内容的提问来源于stack exchange,提问作者Bhuvan Gandhi

