0

I have got a layout component with an Outlet to load the child component.

Layout.tsx

import * as React from "react";
import { createTheme, styled } from "@mui/material/styles";
import Box from "@mui/material/Box";
import style from "./layout.module.scss";
import Drawer from "./Drawer";
import AppBar from "./AppBar";
import Container from "@mui/material/Container";
import Paper from "@mui/material/Paper";
import RecipeLanding from "../pages/Recipe/RecipeLanding";
import { Outlet } from "react-router-dom";
import { Typography } from "@mui/material";

export default function Layout({ children }: any) {
  const [open, setOpen] = React.useState(false);
  const [title,setTitle]=React.useState('');

  const handleTitleUpdate=(t:string)=>{
    setTitle(t);
  }


  return (
    <ThemeContext.Provider value={Theme.background}>
      <RootDiv>
        <AppBar open={open} onDrawerOpen={handleDrawerOpen} />
        <Drawer open={open} onDrawerClose={handleDrawerClose} />
        <Container className="rootContainer" maxWidth="xl" sx={containerStyle}>
          <DrawerHeader />
          <Box sx={{ my: 2 }}>
            <Typography variant="h5">{title}</Typography>
          </Box>
          <Box>
            <Outlet onTitleChange={handleTitleUpdate} />
          </Box>
        </Container>
      </RootDiv>
    </ThemeContext.Provider>
  );
}

It was earlier a child component directly specified instead of outlet. But since navigation is required I added an outlet and expected I can add add onTitleChange property into it. But it doesnt allow I found.

How can achieve this. So my attempt is to update the {title} in layout component when a navigation to child component happens.

Please help

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Sandeep Thomas
  • 4,303
  • 14
  • 61
  • 132

1 Answers1

2

The Outlet component provides a Context. Pass the callback there and access in the nested route's component via the useOutletContext hook.

import { ..., useOutletContext } from 'react-router-dom';

...

type MyContext = {
  onTitleChange: (t: string) => void;
}

export const useMyContext = () => useOutletContext<MyContext>();

...

<Outlet context={{ onTitleChange: handleTitleUpdate }} />

In a page component.

import { useMyContext } from '.....';

...

  const { onTitleChange } = useMyContext();

  useEffect(() => {
    if (/* some condition */) {
      onTitleChange(/* some new title value */);
    }
  }, [/* some dependencies */]);

Edit update-parent-variable-when-child-component-loads-navigate-to

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thanks a lot.. But Sorry to ask how can I pass the parameter from the child component when in loads – Sandeep Thomas Dec 19 '22 at 18:39
  • @SandeepThomas Are you asking for something like `useEffect(() => onTitleChange("...."), []);` in the routed component? – Drew Reese Dec 19 '22 at 19:45
  • Sorry if I confused.. My question was when navigating through different components, the title in parent needs to change. So how can I manage that. Is there is something like component load event or light that?? Sorry I am very new to react or a week experience.. :) – Sandeep Thomas Dec 19 '22 at 20:03
  • @SandeepThomas The `useEffect` hook is guaranteed to run at least once when the component mounts. If you provide an empty dependency array (*i.e. the second argument*) then the effect runs ***only once*** when the component mounts. Here's an [answer](/a/70655615/8690857) of mine implementing similar behavior. – Drew Reese Dec 19 '22 at 20:14
  • THanks a lot. I tried const useTitle = (title:string) => { const { setTitle } = useOutletContext(); useEffect(() => { setTitle(title); }, [setTitle, title]); }; But its saying SetTitle does not exists on type unknown.. :( – Sandeep Thomas Dec 19 '22 at 20:22
  • @SandeepThomas I said it was *similar*. Since you are using Typescript you'll probably need to type the context value you are passing in the `Outlet` similar to my answer here. – Drew Reese Dec 19 '22 at 20:36
  • 1
    @SandeepThomas I've added a running sandbox demo. – Drew Reese Dec 21 '22 at 20:00