简体   繁体   中英

React Router Dom nested routing, or how to pass a slug

I am trying to pass a slug to a header component without having to include the header to each and every subcomponent.

My code looks like this:

const App = () => {
  return (
    <>
      <Grid container direction="column">
        <Grid item>
          <Header />
        </Grid>
        <Grid item container>
          <Grid item xs={false} md={2} />
          <Grid item xs={12} md={8}>
            <Switch>
              <Route exact path="/" component={IntroPage} />
              <Route exact path="/:slug" component={StartPage} />
              <Route path="/:slug/index" component={StartPage} />
              <Route path="/:slug/player" component={PlayerPage} />
              <Route path="/:slug/game" component={GamePage} />
              <Route path="/:slug/scoreboard" component={ScoreboardPage} />
              <Route path="/:slug/controller" component={ControllerPage} />
              <Route path="/:slug/settings" component={SettingsPage} />
              <Route path="/:slug/credits" component={CreditsPage} />
              <Route component={PageNotFound} />
            </Switch>
          </Grid>
          <Grid item xs={false} md={2} />
        </Grid>
        <ToastContainer autoClose={3000} hideProgressBar />
      </Grid>
      <Grid container direction="column">
        <Footer />
      </Grid>
    </>
  );
};

export default App;

What I want is the Header component to know what slug was chosen in one of the routes within the Switch.

Is there a possibility to do this with for example nested routing?

Best regards, Patrick


Edit:

This is my Header component btw:

const Header = (props) => {
  console.log(props.match.params.slug)
  const classes = useStyles();
  const [open, setOpen] = useState();

  const handleDrawerOpen = () => {
    setOpen(true);
  };

  const handleDrawerClose = () => {
    setOpen(false);
  };

  return (
    <div className={classes.root}>
      <AppBar
        position="static"
        className={clsx(classes.appBar, { [classes.appBarShift]: open })}
      >
        <Toolbar>
          <IconButton
            aria-label="open drawer"
            onClick={handleDrawerOpen}
            edge="start"
            className={clsx(classes.menuButton, open && classes.hide)}
          >
            <MenuIcon />
          </IconButton>
          <Typography>Placeholder Banner</Typography>
        </Toolbar>
      </AppBar>
      <Drawer
        variant="persistent"
        anchor="left"
        open={open}
        className={classes.drawer}
        classes={{ paper: classes.drawerPaper }}
      >
        <div className={classes.drawerHeader}>
          <IconButton onClick={handleDrawerClose}>
            <CloseIcon />
          </IconButton>
        </div>
        <Divider />
        <List>
          <ListItem component="a" href="/" button key="start">
            <ListItemIcon>
              <HomeIcon fontSize="large" />
            </ListItemIcon>
            <ListItemText>Start</ListItemText>
          </ListItem>
          <ListItem component="a" href="/game" button key="game">
            <ListItemIcon>
              <SportsEsportsIcon fontSize="large" />
            </ListItemIcon>
            <ListItemText>Game Setup</ListItemText>
          </ListItem>
          <ListItem component="a" href="/player" button key="player">
            <ListItemIcon>
              <PersonIcon fontSize="large" />
            </ListItemIcon>
            <ListItemText>Player Setup</ListItemText>
          </ListItem>
          <ListItem component="a" href="/settings" button key="settings">
            <ListItemIcon>
              <SettingsIcon fontSize="large" />
            </ListItemIcon>
            <ListItemText>Settings</ListItemText>
          </ListItem>
          <ListItem component="a" href="/credits" button key="credits">
            <ListItemIcon>
              <InfoIcon fontSize="large" />
            </ListItemIcon>
            <ListItemText>Credits</ListItemText>
          </ListItem>
        </List>
      </Drawer>
    </div>
  );
};

export default Header;

I want to give the links the ability to include the slug. For example href=/game needs to be href=:slug/game then.

And please be aware that header is loaded before the switch.


Edit2:

At least this:

const Header = (props) => {
  const {match, location, history} = props
  console.log(match)
  console.log(location)
  console.log(history)

....

export default withRouter(Header);

will give me access to the location which has the slug. But this appears to not be the cleanest way. Also match.params does not hold the slug, even with withRouter export. Why is that?

My solution until I know better (Not working)

const Header = (props) => {
  const { location } = props;
  let pathname = location.pathname
  let slug = pathname.split("/")
  console.log(slug[1]);

....


export default withRouter(Header);

This will cause that the url gets confused after a few clicks cause location will be concatenated again and again resulting ins urls like /myslug,game,player,...

Final solution

Well I got the final solution from below which is:

Wrap the Header in its specific location into Route

        <Grid item>
          <Route path={["/:slug", "*"]} component={Header} />
        </Grid>

Then access props.match.params in Header component:

const Header = (props) => {
  console.log(props.match.params)

this will give:

slug: "game"

Issue

The Header isn't being rendered on a route so it doesn't have access to any of the route-props, specifically, the route parameters. It also needs to be rendered on a Route that matches a path that specifies the "slug" route parameter.

Solution

Render the Header into a Route that specifies both a match all path and a path that can match with a slug. Order matter so specify the path with slug first so it can be matched before less specific routes (same as the rules for the Switch ).

<Route path={["/:slug", '*']} component={Header} />

Try using useParams() to get the slug.

Try do like this:

<Route 
   exact 
   path='/:slug/player' 
   render={(props) => <PlayerPage {...props}/>}
/>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM