Search code examples
javascriptreactjsnext.jsreact-props

Props alternative to pass the component state to all the components of application in next.js


I want to pass the setState method of the component (SnackBar) to all the child components of the _app.js. If I pass the setState method of SnackBar to all the child components of _app.js then it will be a very tedious task. Because, there are approx 4 levels of hierarchy from _app.js to the single component node. It includes,

_app.js -> pages -> layouts -> sections -> components

The snippet of _app.js is here.

function MyApp({ Component, pageProps }) {

    const [ toastOpen, setToastOpen ] = React.useState({
        msg: '',
        open: false
    });

    React.useEffect(() => {
        pageProps = { ...pageProps, setToastOpen };
    }, []);

    return (
        <React.Fragment>
            <ToastMessage
                message={ toastOpen.msg }
                setOpenState={ setToastOpen }
                openState={ toastOpen.open }
            />
            <Component {...pageProps} />
        </React.Fragment>
    )
}

Is there any way that I can directly import the setToastOpen method in the child component and use it whenever I need it?


Solution

  • React have a special Feature called Context Api , using that you can skip the props chain passed into your components..

    I recomend you to checkout below resources to learn about context Api -


    Example of ContextAPI

    Create a seperate file for Context Toast-context.js , You can use any name you want.

    import React, { useState } from "react"
        const ToastContext = React.createContext({
        message: "",
        toastOpen: false,
        toggleToast: () => { },
        changeMessage: () => { }
    })
    
    export const ToastContextProvider = ({children}) => { 
    /*you need to use 
    this component to wrap App.js so that the App.js and all of its children 
    and their children components and so on will get the access to the 
    context*/
    
        const [toastOpen, setToastOpen] = useState(false);
        const [message, setMessage] = useState("");
    
        const toggleToast = () => {
        setToastOpen(true)
        }
    
        const changeMessage = (message) => {
        setMessage(message);
        }
    
        return (
            <ToastContext.Provider value={
                toastOpen,
                message,
                toggleToast,
                changeMessage
            }>
                {children}
            </ToastContext.Provider>
        )
    }
    

    now in the App.js file you need to wrap your components with ToastContextProvider component

    import React, { useContext } from "react";
    import { ToastContextProvider } from "./Toast-context";
    import { ToastContext } from "./Toast-context";
    
    function MyApp({ Component, pageProps }) {
        const { message, toastOpen, toggleToast, changeMessage } =
            useContext(ToastContext);
    
        return (
            <ToastContextProvider>
                {toastOpen && <div className="toast">{message}</div>}
            </ToastContextProvider>
        );
    }
    

    just import the context using useContext Hook in any component you want. you don't need to wrap with <ToastContextProvider> in every component. just use useContext hook and then you can see the state and as well as call the functions methods to change the state.

    Also make sure to refer the above links to learn more about Context Api. Thank You