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>
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>
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>
)
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>
)
At the end remember to:
- Make sure you have imported any components you are routing to.
- 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
}
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:
- Get the text from the input
- 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:
Add a prop on the input tag:
ref={this.myInput}
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)