skip to Main Content

I am using a combination of react-router-dom and Material-UI in my app. I’ve tried to make a very basic example below with the following files:

routes.tsx

import { Outlet, createBrowserRouter } from "react-router-dom"

const App = () => {
  return (
    <BaseLayout>
      <Outlet />
    </BaseLayout>
  )
}

export const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
    children: [
      { index: true, element: <Home /> },
    ],
  },
])

BaseLayout.tsx

export const BaseLayout = {children} => {
  return (
    <>
      <head />
      <main>{children}</main>
      <footer />
      <Alert />
    </>
  )
}

Home.tsx

export const Home = () => (
  <>
    <button /> // <-- I want this button to trigger the Alert and set open to true !
  </>
)

Alert.tsx

import { Alert as MuiAlert, Snackbar } from "@mui/material"

export const Alert = ({open?, severity?, message?}) => {
  return (
    <Snackbar
      open={open}
      autoHideDuration={500}
      // onClose={handleClose}
      // action={action}
    >
      <MuiAlert severity={severity}>{message}</MuiAlert>
    </Snackbar>
  )
})

I am trying to have a single Alert component shared across the application that can trigger an alert for different reasons.

I am not sure how to approach it.

3

Answers


  1. you just import your Alert component in any other components in which you may show the Alert. in the parent component you have to create a state to control the alert visibility:

    import Alert from "./Alert";
    //...
    const Parent = () => {
      const [isAlertOpen, setIsAlertOpen] = useState(false);
      const closeAlert = () => {
        setIsAlertOpen(false);
      };
    
      return (
        <>
          //....
          <Alert
            open={isAlertOpen}
            autoHideDuration={500}
            onClose={closeAlert}
            severity={severity}
            message="we are showing the Alert from the parent page"
          />
        </>
      );
    }
    

    finally to show the Alert component, all you have to do is to set isAlertOpen to true:

    <>
      <button onClick={() => setIsAlertOpen(true)} /> 
    </>
    
    Login or Signup to reply.
  2. I would suggest keeping some state in the BaseLayout component for the open/close status of the alert, and move the Outlet into BaseLayout and provide this state and setter on the outlet’s context provider. The Home component would use the useOutletContext hook to access the provided context value.

    import { Outlet } from 'react-router-dom';
    
    export const BaseLayout = () => {
      const [alertProps, setAlertProps] = React.useState({});
    
      const onAlertClose = () => setAlertProps({});
    
      return (
        <>
          <head />
          <main>
            <Outlet context={{ setAlertProps }} />
          </main>
          <footer />
          <Alert {...alertProps} handleClose={onAlertClose} />
        </>
      )
    }
    
    import { useOutletContext } from 'react-router-dom';
    
    export const Home = () => {
      const { setAlertProps } = useOutletContext();
    
      const openAlert = () => {
        setAlertProps({
          open: true,
          severity: "success",
          message: "Alert is opened!",
        });
      };
    
      return (
        <>
          <button onClick={openAlert}>
            Alert!
          </button> 
        </>
      )
    };
    
    export const router = createBrowserRouter([
      {
        path: "/",
        element: <BaseLayout />,
        children: [
          { index: true, element: <Home /> },
        ],
      },
    ])
    
    Login or Signup to reply.
  3. I think you should use Context.
    You can read more at https://react.dev/reference/react/useContext

    For example you can create a alterProvider and in that file will there

    const AlertContext= React.createContext()
    
    function alertReducer(state, action) { //for update state in context
     switch (action.type) {
      //logic here
     }
    }
    
    function AlertProvide({children, initialValue, onChange}) {
     const [state, dispatch] = React.useReducer(
        filterReducer,
        typeof initialValue === 'function' ? initialValue() : initialValue,
      )
    
      useEffectAfterMount(() => {
        if (typeof onChange === 'function') {
          onChange(state)
        }
      }, [state])
    
      const value = { state, dispatch, columns }
      return (
        <AlertContext.Provider value={value}>
          {children}
        </AlertContext.Provider>
      )
    }
    
    function useAlert() {
      const context = React.useContext(AlertContext)
      if (context === undefined) {
        throw new Error(`useAlertmust be used within a AlertProvide`)
      }
      return context
    }
    

    or you just can use any globle client state managerment like Redux or Mobx.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search