简体   繁体   中英

subpage doesn't match react-router 4

I have my router setup like this

<Switch>
  <PrivatePage key={index} {...opts} component={(props) => 
     <Section {...props} pages={childRoutes} />
  } />
  <PrivatePage path='/accounts/:id' exact={true}  render={({ match }) => (
    <Redirect to={/accounts/${match.params.id}/profile} />
  )} />
  ...
  <Route component={NotFound} />
</Switch>

And then <Section />

<SubNavMenu />
<Route path=/accounts/:id/profile componet={ProfilePage} />
<Route path=/accounts/:id/dashboard componet={DashboardPage} />

And then <PrivatePage /> renders like, while <Page /> just renders <Navigation /> {this.props.children}

const PrivatePage = ({ component: Component, ...rest }) => {
  let result = props => (
    <Redirect
      to={{
        pathname: '/redirect',
        state: { from: props.location },
      }}
    />
  )

  if (User.methods.isAuthed()) {
    result = props => (
      <Page>
        <Component {...props} />
      </Page>
    )
  } else if (rest.path === '/') {
    result = props => (
      <Redirect
        to={{
          pathname: '/login',
        }}
      />
    )
  }

  return <Route {...rest} render={props => result(props)} />
}

export default PrivatePage

Clicking a link that takes me to accounts/:id properly redirects me to the profile page, but when I try to go to the dashboard page from the SubNavMenu I get my NotFound page and consoling this.props.match {path: "/", url: "/", params: {…}, isExact: false} but my path is /accounts/7kB7fRdsu39Be44ou/dashboard

Thank you for your help


Per request, full code of Section

pages = [
{
  authed: true,
  icon: 'cog',
  component: (<div/>),
  name: 'AccountDetailSection',
  path: `/accounts/:id/profile`, 
},
{
  authed: true,
  component: AccountProfilePage,
  exact: true, 
  getLink: id => `/accounts/${id}/profile`, 
  icon: 'cog',
  label: 'Account',
  name: 'AccountDetailProfile',
  parent: 'AccountDetailSection',
  path: `/accounts/:id/profile`, 
},
{
  authed: true,
  component: AccountDashboardsPage,
  exact: true, 
  getLink: id => `/accounts/${id}/dashboard`, 
  icon: 'cog',
  label: 'Dashboard', 
  name: 'AccountDetailDashboards',
  parent: 'AccountDetailSection',
  path: `/accounts/:id/dashboard`, 
},
]


class PrivateSection extends React.Component<IProps, IState> {
  classes = { // static values
    button:  'App-navigation--listItemButton',
    container: 'App-navigation',
    header: 'App-navigation--header',
    headerLogo: 'App-navigation--headerLogo',
    listContainer: 'App-navigation--list',
    listItem: 'App-navigation--listItem',
    listItemActive: 'App-subnavigation--listItem--active',
    listItemHover: 'App-navigation--listItem--hover',
    positionBottom: 'App-navigation--bottom',
    positionTop: 'App-navigation--top',
  }
  sharedProps = { // static values
    activeClass: this.classes.listItemActive,
    buttonClass:  this.classes.button,
    buttonContainer: this.classes.listItem,
    hoverClass: this.classes.listItemHover,
    menuContainer: this.classes.listContainer,
    onHover: this.handleMouseIn.bind(this),
  }

  constructor(props: IProps) {
    super(props) 

    this.state = {
      hovering: '',
    }
  }

  handleMouseIn(name: string) {
    this.setState({hovering: name})
  }
  handleMouseOut() {
    this.setState({hovering: ''})
  }

  renderSubNav() {
    const navOpts = {
      hovering: this.state && this.state.hovering || '',      
      onHover: this.handleMouseIn.bind(this),
    }

    const navItems: any = this.props.pages.map(p => { // tslint:disable-line no-any
      const o = {...p}

      if (typeof(o.getLink) === 'function') {
        const {id} = this.props.match && this.props.match.params || {id: ''}

        o.link = o.getLink(id)
        o.getLink = undefined
      }

      o.authed = undefined
      o.exact = undefined
      o.component = undefined

      return {...navOpts, ...o}
    })

    const submenuClasses = {
      active: this.sharedProps.activeClass,
      button:  this.sharedProps.buttonClass,
      buttonContainer: this.sharedProps.buttonContainer,
      hover: this.sharedProps.hoverClass,
      menuContainer: this.sharedProps.menuContainer,
    }

    return (
      <div
        className='profile_subnav'
        style={{height: '100%'}}
        onMouseLeave={() => this.handleMouseOut()}
      >
        <Menu
          items={navItems}
          classes={submenuClasses}
        />
      </div>
    )
  }
  renderContent() {
    return (
      <div className='profile_content'>
        {this.props.pages.map((opts, index) => {
          const o: any = {...opts} // tslint:disable-line no-any
          if (typeof(o.getLink) === 'function') {
            const {id} = this.props.match && this.props.match.params || {id: ''}

            o.link = o.getLink(id)
            o.getLink = undefined
          }

          return (
            <PrivateRoute key={index} {...o}/>
          )
        })}
      </div>
    )
  }
  render() {
    return (
      <div
        className='page--content_container'
      >
        {this.renderSubNav()}
        {this.renderContent()}
      </div>
    )
  }
}

export default PrivateSection

Render method of <Button /> (wrapped by <Menu />

 render() {
    const {
      activeClass,
      containerClass,
      exactLink,
      hoverClass,
      icon,
      label,
      link,
      onClick,
      handleActive,
    } = this.props

    let message = (
      <div className='Button--message'>
        <div className='Button--messageText'>{label}</div>
      </div>
    )   
    if (icon) {
      message = (
        <div className='Button--message'>
          <div className='Button--messageIcon'><Icon name={icon} / ></div>
          <div className='Button--messageText'>{label}</div>
        </div>
      )    
    }

    const buttonContainerClass = this.isHovering() ? `${containerClass} ${hoverClass}` : containerClass

    const ButtonContainer = props => (
      <button
        {...props}
        className={this.props.buttonClass || ''}
        onMouseEnter={() => this.handleMouseIn()}
        onMouseLeave={() => this.handleMouseOut()}
      >
        {message}
      </button> 
    )

    let Result
    if (typeof(link) === 'string') {
      if (typeof(activeClass) === 'string' && activeClass.length > 0) {
        const opts = {
          activeClassName: activeClass || '',
          className: buttonContainerClass || '',
          exact: exactLink || false,
          isActive: handleActive || undefined,
          strict: true,
          to: link,
        }

        Result = (
          <NavLink {...opts} >
            <ButtonContainer />
          </NavLink>
        )
      } else {
        Result = (
          <Link to={link} className={buttonContainerClass}>
            <ButtonContainer />
          </Link>
        )
      }

    } else if (typeof(onClick) === 'function') {
      Result = (
        <div className={buttonContainerClass}>
          <ButtonContainer onClick={() => onClick()} />
        </div>
      )

    } else {
      console.warn('Button must have an action props> ', {props: this.props})
    }

    return Result
  }

I had a similar issue where Switch wasn't finding routes that were wrapped with other components. Looking at the source, it looks like Switch does not look for Route s in the children recursively, so they can't be nested.

That being the case, to use Switch you'd need to refactor to have Route be the top-level component for each route. Or refactor to not use Switch - make all the routes exact matches, basically.

Switch source: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Switch.js It uses React.Children.forEach to look for paths, which only iterates over the immediate children, not nested children.

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