DEV Community

Lukie Kang
Lukie Kang

Posted on

React Learnings from 2018 - Part 3: Props and Routing

In the last post we set up a bunch of components, now we need to look at how they can talk to each other. This relies on Props and State.

Props

When you have an image tag you will use multiple attributes like: <img class="etc" src="url" alt="something"> So that the image tag has the information it needs.

In React, props are those attributes, and a way to get data into that component. So if we want to pass data from App to Header we can do this via props.

In header.js we have the following snippet of code:

<h3 className="tagline">
    <span>Great tasting stuff</span>
</h3>
Enter fullscreen mode Exit fullscreen mode

If we want to pass in a new tagline from App.js we can change the Header component tag to include a prop:

<Header tagline="Terrible Food" />

If you now look in React Dev Tools you will see the tagline prop when you select Header.

How do you use the prop?

<h3 className="tagline">
    <span>{this.props.tagline}</span>
</h3>
Enter fullscreen mode Exit fullscreen mode

A component is an object, so we can just pull information from the props object within it. JSX needs to use curly braces when we need to speak in pure JS by the way.

Stateless Functional Components

I mentioned functional components in an earlier post. As a recap, if all a component is doing is rendering something then it doesnt need to have all the functionality of a class based component.

Don't use a sledgehammer when a regular one will do I guess. It is just a regular function, however this wont work the same way so we can just pass it to the function:

const Header = props => (
    <span>{props.tagline}</span>
)
Enter fullscreen mode Exit fullscreen mode

React Router

Routing is all about pointing to certain urls with certain content. If you have used Express you may have already had experience in setting up a route to render a certain view for instance.

You need a router to do something depending on the URL the browser uses.

The routing doesn't come build into react, there are a couple of different routers out there. The two most popular are:

-React Router
-Next.js

We are using React Router for this example.

Setting up the router

Since everything is a component in React, we need to set up a Router.js component

If you recall when we had a list of dependancies we had one called react-router-dom this is what we are going to use here. It has a number of components since it handles routing for React Native too.

For our purposes we need:

  • BrowserRouter
  • Switch
  • Route

Route

The route component takes in several props, the ones we most care about are:

path - This states the URL path this applies to (e.g "/")

exact - specifies that the path needs to be an exact match

component - What component to use, this needs {};

Building our router component

First we import them: {BrowserRouter, Route, Switch} from 'react-router-dom'. We also need to import React for JSX.

Then lets build our router function, this can be a stateless functional component:

const Router = () => (  {/* Clever way of returning */}
    <BrowserRouter>
        <Switch>
            <Route exact path="/" component={storePicker}/>
            <Route path="/store/:storeId" component={App}/>
            <Route component={NotFound}/> {/* Our 404 type page */}
        </Switch>
    </BrowserRouter>
)
Enter fullscreen mode Exit fullscreen mode

At the end remember to:

  1. Make sure you have imported any components you are routing to.
  2. Add export default Router to export it!

When we are done, in our app.js lets import router by doing: import Router from "/.components/Router" Make sure to also use the Router tag in place of any placeholder you might use.

Create any new components you have made, such as the NotFound component.

You now should have a working router! Note that in the props, when we look at a component, we get some new props such params which is useful when it comes to routing.

Sidenote : What is export default anyhow?

When we specify export default <function>, the corresponding import will take that function if we dont specify.

However we can have a file with a bunch of functions and pull out what we need with destructuring. For example if you have a helpers.js file with functions they can be exported with the export command:

export function example(){
    return someFunctionality
}
Enter fullscreen mode Exit fullscreen mode

in the file we need the function we can import it:

import { example } from "../helpers"

Random Note: use defaultValue when setting a default value on an input, as opposed to value. React doesnt like it otherwise

Conclusion

We now can have a basic multipage react application with different components being loaded based on the URL via React Router

We can also pass values from the parent component to the children via Props

In the next section we delve into Events and how things can change based upon user input.

ChAPTER 21 - Events and Refs

Events are how React functions when something happens, such as a button being clicked or a form being submitted. They are used quite a bit in JS, you might know that.

One main difference in React is they are done inline:

<button onClick={this.handleClick}>Click Me</button>

Where handleClick is a function availble to the component. there are alot of 'on' events to choose from. Note you do not include paranthesis, else it will run on mount.

Submitting a Form

Like the button, we include an event on the form tag:

<form onSubmit={this.handleSubmit}>

Let's assume the handleSubmit method is just a console.log("Hi") for now. We should expect 'Hi' right?

Nope, because the default behavior is to refresh the page

We can pass the event object to the method to allow handleSubmit to stop the refreshing behaviour with the following method:

event.preventDefault();

Now we will see the console.log as the page wont refresh.

Taking an input and Routing based on that

In this example, let's assume we want a form that goes to a user profile based on an input's value.

The method currently looks like this:

goToProfile(event){
    event.preventDefault()

}

We need to do two things:

  1. Get the text from the input
  2. Change the page to /profile/INPUTVALUE

Getting the text from an input properly

We don't want to just pick it up from the DOM, the DOM should be result, not take part in performing a task. Well there is two ways we can do this. First is state which we will get to and generally is the prefered method, but another way is refs which we will use this time around.

Refs

A ref, references a DOM element so a method can do things with it. This has had many forms over the years but this implementation is the most modern...at tiem of writing:

  1. Add a prop on the input tag: ref={this.myInput}

  2. Create a ref at the start of the component: myInput = React.createRef();

That's how we ref nowadaya. Now we can reference the dom node in any methods on the component. Yay!

Wait no we can't...just doing this within a method however will cause an error! It won't be able to find the 'this' that has myInput. This is quite wierd. this should be the component right?

"this" and Binding Methods

Let's take a slight aside...

Well react has binding methods, these built-in methods bind this to the component, normally this is what we use. However a component's own unique methods dont have that binding by default because the components extend React.Component This does not automagically pass to methods we make ourselves in a component.

A solution to this is to bind our own methods, there is two ways to do this:

A constructor function - hopefully you recall from ES6 classes we can make a constructor function and define our methods in there:

constructor(){
    super();
    this.goToProfile = this.goToProfile.bind(this)
}

Wow that is confusing, but it makes sure that the goToProfile's 'this' is the 'this' of the constructor which is the component...ouch my poor head!

A new way (which is probably going to be old by the time you see it) is changing the method to a property that is an arrow function

Old Method: goToProfile(event) {}

New Property: goToProfile = (event) => {};

The arrow function binds this

In short, if you want to access this inside a custom method, turn it into a property with a fat arrow function. Something like this:

nameRef = React.createRef();
createProfile = event => {
        event.preventDefault();
        const profile = {
            name: this.nameRef.value.value
            //Etc. Now nameRef can be picked up and used... why is it value value? Read on...
}

getting a value from an input

Now the component is capturing details about the input when there is a form submit via a ref.

When the goToProfile method is run, we have asked it to console.log(this), the this here is the component. But we can drill deeper:

  • this.myInput will return the reference
  • this.myInput.current will return the input for which the ref was placed.
  • this.myInput.current.value will return the value of the input

As you can see there is quite a few layers involved here! It's a good idea to put this into a variable when we use to to go to that URL.

Change the URL without refreshing the page using Push State and React Router

Now we can access the data from the form submission we can use it to direct us to the relevent URL.

To do this we use a React Router method called push. We have access to React Router since StorePicker is a child of the Router component, so when we look in react dev tools we can see the props that are given by React Router. Push method is actually in an object called history, so the command we want is:

this.props.history.push(`/profiles/${profileName}`)

Note the backticks, and dollar sign. We are using a template string here.

And success we will route to /profiles/profileName where profileName is the variable recording the value of the input which is the value of the ref which is on the component.... that simple!

Note how fast it is, react router just needs to swap out the component, and doesnt need to load the whole page. Thats pretty cool!

Top comments (0)