0

I'm new to React Router so if this has been asked before maybe someone could point me in the right direction! Basically I have a WordPress install that I'm pulling in my websites data from through the API.

I've created custom routes to query my pages and my posts by slug.

Using react router I was able to create a template called Page.js which changes dynamically using the code below.

However, now I'm trying to do the same exact thing with the blog posts but the app isn't using Blog.js its still defaulting back to Page.js

here's my App.js code...

import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from './pages/Home';
import Page from './pages/Page';
import Blog from './pages/Blog';
import Header from './components/Header';
import Footer from './components/Footer';

class App extends React.Component {
  render() {
    return (
      <Router>
      <div>
          <Header/>
          <Route exact path="/" component={Home} />
          <Route path="/:slug" component={Page} />
          <Route path="/blog/:slug" component={Blog} />
          <Footer/>
      </div>
      </Router>
    );
  }
}

export default App;

More Details:

Page.js works by checking const { slug } = this.props.match.params; and then querying WordPress using that slug to pull in the data it needs. In componentDidUpdate i'm checking prevProps to see if the slug matches the previous slug, if not it fetching the new data.

This works great and I was hoping to do the same in the Blog.js as well.

However, if this isn't the best approach please advise another method.

WordPressFreddie
  • 129
  • 2
  • 12

2 Answers2

2

Two things:

  1. Use element: This will allow only one route to be used, no composing. (See this documentation)
  2. Check the order of path statements: Use defined paths before :param, this avoids considering /blog/:slug as a /:slug parameter.

`

import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
class App extends React.Component {
  render() {
    return (
      <Router>
      <div>
          <Header/>
          <Switch>
              <Route exact path="/" component={Home} />
              <Route path="/blog/:slug" component={Blog} />
              <Route path="/:slug" component={Page} />
          </Switch>
          <Footer/>
      </div>
      </Router>
    );
  }
}
Gabriel Balsa Cantú
  • 1,523
  • 11
  • 10
1

I think you're pretty close to the recommended implementation, just a few small tweaks should get you there.

First,
In your App.js file you're actually handling routing, without using the <Switch> component provided by React Router, replacing the <div> and </div> tags in your App.js file with <Switch> and </Switch> respectively should get this working for you. See below...

import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; //make sure you import it also!
import Home from './pages/Home';
import Page from './pages/Page';
import Blog from './pages/Blog';
import Header from './components/Header';
import Footer from './components/Footer';

class App extends React.Component {
  render() {
     return (
      <Router>
        <Switch> //Add this in
          <Header />
          <Route exact path="/" component={Home} />
          <Route path="/blog/:slug" component={Blog} />
          <Route path="/:slug" component={Page} />
          <Footer />
       </Switch> //Add this in
     </Router>
    );
  }
}

export default App;



I would recommend going further though!

To make these components more understandable, you should refactor routing functionality into a routes.js file, and top-level App component logic/structure into the App.js file. See below...


In App.js:

This file is where you should handle your base application structure and logic. For example this file is where you'll import your <Header>, your <Footer>, and where the Route component will render.

import * as React from 'react'
import Header from './../Header/Header.jsx'
import Footer from './../Footer/Footer.jsx'

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
         // Handle your top-level application state here
        }
    }

    // define your top-level application functions here

    render() {
        return (
            <div>
                <Header />
                <main>
                    {this.props.children} //This where the Route components will render
                </main>
                <Footer />
            </div>
        )
    }
}

export default App


In Routes.js:

This file is where you should import your App component, and then handle the routing statements.

import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'

import App from './components/App'
import Home from './pages/Home'
import Page from './pages/Page'
import Blog from './pages/Blog'

/* construct routes */
export default () => {
    return (
        <Router>
            <App>
                <Switch>
                    <Route path='/' exact component={Home} />
                    <Route path='/blog/:slug' component={Blog} />
                    <Route path='/:slug' component={Page} />
                </Switch>
            </App>
        </Router>
    )
}

If you structure your application this way, your routing logic and top-level application logic are separate, and in the end your files will be less cluttered as both Route files and top-level App files can get fairly dense.

Hope this helps! Let me know if I can explain anything further.

Mike Abeln
  • 1,763
  • 6
  • 19
  • 1
    Mike, thats exactly what my next plan was. I could just tell by looking at examples with react router that I would be better suited with a component that handled the logic of the routes. I've implemented these changes and they work great! – WordPressFreddie Oct 08 '18 at 19:11