Giter Site home page Giter Site logo

react-components-as-routes-v-000's Introduction

Introduction to React Router

Objectives

  1. Describe React Router's approach to client-side routing
  2. Explain how React Router allows building a route tree as a component
  3. Describe how routes are matched in React Router

A complicated world needs an address

So far, we have been building our applications without any navigation, so everything in the app has lived at the same url. Now we can make it look like we are changing the page, by showing or hiding some components, but none of these changes are dependent on a change in the url.

Now this may seem like a small quibble, but web addresses are the backbone of the Internet. Think about it, the web is just a series of links to other pages. And now instead of sharing a link to a list of users, we currently only provide a link to our homepage, and then would require some showing or hiding to a list of users.

React Router is a routing library for React that allows us to link to specific urls then show or hide various components depending on which url is displayed. As React Router's documentation states:

Components are the heart of React's powerful, declarative programming model. React Router is a collection of navigational components that compose declaratively with your application. Whether you want to have bookmarkable URLs for your web app or a composable way to navigate in React Native, React Router works wherever React is rendering--so take your pick!

For this README we will be building our first Component routes as a Code Along

Code Along

Step 1: Setting up our Main Route

Note Make sure you clone down this repo, run npm install && npm start, and open http://localhost:3000 in the browser.

If you open up the src/index.js file you will see that currently we are defining an App component, and then rendering that component in the Dom.

// ./src/index.js

import React from 'react';
import ReactDOM from 'react-dom';

const App = () => {
  return (
    <div>
    </div>
  );
};

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

With React Router our core app routing will live in this component. We will define our navbar and our various routes within this file. Let's install that package now.

npm install react-router-dom

We now want to import the BrowserRouter component as a Router and the Route component and inject it into our App component.

// .src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
// Step 1. Import react-router functions
import { BrowserRouter as Router, Route } from 'react-router-dom';

const App = () => {
  return (
    <div>
      <h1>Home</h1>
    </div>
  );
};

// Step 2. Changed to have router coordinate what is displayed
ReactDOM.render((
  <Router>
    <Route path="/" component={App} />
  </Router>), 
  document.getElementById('root')
);

Step 1: In Step 1 above, there are two components that we are importing from React Router. We use them in turn.

Step 2: The Router (aka BrowserRouter) component is the base for our apps routing. It is where we declare how React Router will be used. For example, notice that nested inside the Router component we use the Route component. The Route component is in charge of saying, when the url matches this specified path, render this specified component. We are using the render prop in the Route component, but we could have used component={Home} instead. With render we are just invoking a function call to render <div><h1>Home</h1></div>.

Let's try it. Run npm start to boot up the application and then point your url to localhost:3000. What you'll notice is that when you type in the url; it will render a <div>Home</div>.

Adding Additional Routes

In the last two steps we learned how to set up the basic Router component and inject our very first Route component. Let's continue down the rabit hole and add routes for an about page and a login page.

In our `/src/index.js file we should now have the following code:

// ./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom';

const App = () => {
  return (
    <div>
      <Router>
        <Route exact path="/" render={() => <h1>Home</h1>} />
      </Router>
    </div>
  );
};

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

Let's add our /about and /login routes

// ./src/index.js

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

const App = () => {
  return (
    <div>
      <Router>
        <Route exact path="/" render={() => <h1>Home</h1>} />
        {/* add in this code */}
        <Route exact path="/about" render={() => <h1>About</h1>} />
        <Route exact path="/login" render={() => 
          <div>
            <form>
              <div>
                <input type="text" name="username" placeholder="Username" />
                <label htmlFor="username">Username</label>
              </div>
              <div>
                <input type="password" name="password" placeholder="Password" />
                <label htmlFor="password">Password</label>
              </div>
              <input type="submit" value="Login" />
            </form>
          </div>
        } />
      </Router>
    </div>
  );
};

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

Reload your browser and look at our beautiful routes, but..... oh wait....?!?! Where is the rendered content?

If you open up your browser dev tools console You should be seeing the error: Uncaught Error: A <Router> may have only one child element. What does this mean? Well a Router component can only have one child, but we just gave it 3 children. To remedy this problem we need to place all of the Route components into a <div> tag. Lets take the parent <div> tag and nest it inside of the Router component instead like this:

return (
  <Router>
    {/* move the <div> tag to be nested inside of the <Router> component */}
    <div>
      <Route exact path="/" render={() => <h1>Home</h1>} />
      
      <Route exact path="/about" render={() => <h1>About</h1>} />
      <Route exact path="/login" render={() => 
        <div>
          <h1>Login</h1>
          <form>
            <div>
              <input type="text" name="username" placeholder="Username" />
              <label htmlFor="username">Username</label>
            </div>
            <div>
              <input type="password" name="password" placeholder="Password" />
              <label htmlFor="password">Password</label>
            </div>
            <input type="submit" value="Login" />
          </form>
        </div>
      } />
    </div>
  </Router>
);

Let's go back to the browser and manualy type in the url locations for /, /about & /login. Do you see the rendered h1 tags for the / and /about urls? What about the form when you go to /login?

Just to recap what we have done so far: We imported the react-router-dom node module into our index.js with the BrowserRouter as Router & the Route components. After importing these into the file we returned the Router component as the top level tag in our JSX return statement with a proceding div tag that contained our 3 children route components. Each route is doing 3 things right now:

  • setting a path `path="/about"
  • passing a arrow function inside of a render prop to render some JSX
  • setting an attribute of exact, which explicitly states that you will only see the rendered JSX if you go to /about not /about/something_else.

We have made great progress, but this doesn't seem like it is managable long term. What if we have 20 routes, do we render the JSX inline for each Route? We should fix that.

Components as Props

In the above step we just used the render prop to invoke some JSX code to be rendered. This is great for small simple apps, but we want our code to be reusable and less britle. Let's move this code into small components and inject them into the render prop.

// ./src/index.js 

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

const Home = () => <h1>Home</h1>;

const About = () => <h1>About</h1>;

const Login = () => 
  <div>
    <h1>Login</h1>
    <form>
      <div>
        <input type="text" name="username" placeholder="Username" />
        <label htmlFor="username">Username</label>
      </div>
      <div>
        <input type="password" name="password" placeholder="Password" />
        <label htmlFor="password">Password</label>
      </div>
      <input type="submit" value="Login" />
    </form>
  </div>;

const App = () => {
  return (
    <Router>
      <div>
        <Route exact path="/" render={Home} />
        <Route exact path="/about" render={About} />
        <Route exact path="/login" render={Login} />
      </div>
    </Router>
  );
};

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

After your finished, refresh the browser and verify that it is still working. So before we congratulate ourselves yet go take a look at the Route component documentation and see if there is a prop that is better suited for this.

https://reacttraining.com/react-router/web/api/Route

Yep, that is right! The Route component API has a prop called component. This is more declaritive and it also uses the React.createElement instead of inline JSX injection. We should change our code to use this now.

<Router>
  <div>
    <Route exact path="/" component={Home} />
    <Route exact path="/about" component={About} />
    <Route exact path="/login" component={Login} />
  </div>
</Router>

You can verify that everything is working as it should, in the browser.

So now we have a Router component with Routes that invoke components. What are we missing?

NavLinks

What good are routes, if users don't know how to find them or what they are?

The React Router API comes with two options of adding in Links: and . The both have the same base level functionality that will update the browser url and render the Route component, but comes with some additional features that are great for navbars like:

  • activeClassName for when a link is active and you want additional styling using html classes.
  • activeStyle if you want to do inline styling.
  • isActive if you want to add aditional logic to your application to state which link is currently active. This prop can invoke a function call. isActive={() => doSomething}

There are some additional attributes too, but those are the 3 to get comfortable with.

So now that we know what component to use lets add them into our application.

import React from 'react';
import ReactDOM from 'react-dom';
/* Add NavLink to importer */
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom';

/* Add basic styling for NavLinks */
const link = {
  width: '100px',
  padding: '12px',
  margin: '0 6px 6px',
  background: 'blue',
  textDecoration: 'none',
  color: 'white',
}

/* add the navbar component */
const Navbar = () => 
  <div>
    <NavLink 
      to="/"
      /* set exact so it knows only to only set activeStyle when route is deeply equal to link */
      exact
      /* add styling to Navlink */
      style={link}
      /* add prop for activeStyle */
      activeStyle={{
        background: 'darkblue'
      }}
    >Home</NavLink>
    <NavLink 
      to="/about"
      exact
      style={link}
      activeStyle={{
        background: 'darkblue'
      }}
    >About</NavLink>
    <NavLink 
      to="/login"
      exact
      style={link}
      activeStyle={{
        background: 'darkblue'
      }}
    >Login</NavLink>
  </div>;

const Home = () => <h1>Home</h1>;

const About = () => <h1>About</h1>;

const Login = () => 
  <form>
    <h1>Login</h1>
    <div>
      <input type="text" name="username" placeholder="Username" />
      <label htmlFor="username">Username</label>
    </div>
    <div>
      <input type="password" name="password" placeholder="Password" />
      <label htmlFor="password">Password</label>
    </div>
    <input type="submit" value="Login" />
  </form>;

const App = () => {
  return (
    <Router>
      <div>
        <Navbar />
        <Route exact path="/" render={Home} />
        <Route exact path="/about" render={About} />
        <Route exact path="/login" render={Login} />
      </div>
    </Router>
  );
};

ReactDOM.render(
  <App />, 
  document.getElementById('root')
);

Load up the browser again and you should see beautiful blue navlinks that load up the desired component. To get more comfortable, I would recommend implementing /signup and /messages routes that load in compnents, also make sure to add in some NavLinks.

Resources

react-components-as-routes-v-000's People

Contributors

lukeghenco avatar jeffkatzy avatar antoinfive avatar

Watchers

James Cloos avatar Michael Ries avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.