Search code examples
reactjsnext.jsreact-propsreact-state-management

Next.js Lifting state up


_app.js

function MyApp({ Component, pageProps }) {
    return (
        <Provider store={store}>      
            <Layout>
                <Component {...pageProps} />
            </Layout>
    );
}

export default MyApp;

Layout.js

function Layout({ children }) {
    const [cartOpen, setCartOpen] = useState(false);
    const handleOpen = () => setCartOpen(!cartOpen);

    return (
        <>
            <Cart cartOpen={cartOpen} handleOpen={handleOpen} />
            <main>{children}</main>
        </>  
    )
}

ProductPage.js

function ProductPage(props) {
    return (
        <div>
            <button onClick={() => console.log('set state to true in cartOpen(defined in layout.js)')}
        </div>
    )
}

Inside the ProductPage component which is the child component of Layout, I want an element to have a OnClick Event handler which will change state in Layout component to setCartOpen(true)

Link to sandbox


Solution

  • You can leverage React Context to make setCartOpen available to any components down the tree.

    import React, { createContext, useState } from 'react';
    
    export const CartContext = createContext(null);
    
    function Layout({ children }) {
        const [cartOpen, setCartOpen] = useState(false);
        const handleOpen = () => setCartOpen(!cartOpen);
    
        return (
            <CartContext.Provider value={{ cartOpen, setCartOpen }}>
                <Cart cartOpen={cartOpen} handleOpen={handleOpen} />
                <main>{children}</main>
            </CartContext.Provider>  
        )
    }
    
    export default Layout;
    

    Then, in your page, just retrieve setCartOpen from context and use it.

    import { CartContext } from '<your-path-to>/Layout';
    
    function ProductPage(props) {
        const { setCartOpen } = useContext(CartContext);
    
        return (
            <div>
                <button onClick={() => setCartOpen(true)}>Open Cart</button>
            </div>
        );
    }
    
    export default ProductPage;