We’ve all seen and read the React tutorials. We know about classes and components and JSX and whatnot, but then there comes the time when you have to start doing things for real. “Real?” you say. Yes. Like connecting to a database or navigating around something larger than “Hello World.” Oh, then there’s that dreaded state thing. Well, let’s have a quick talk about the “Extras” that we can add to a React application.
In this blog, we will address some of React’s Extras, like adding routing using the React Router, adding data access using the JavaScript Fetch API, and creating a global state management feature using React’s built-in Context API.
Let’s Address Routing in React
At some point in time, you will have the need for additional pages in a web application. To handle this, web developers will need to use routing to create and manage those page requests. Routing is a browser mechanism where HTTP requests are direct to the code that will handle them. This is commonly referred to as routes.
Here’s a common example:
So, let’s look at some code where we setup Routing inside of a React application using the common React extra, a third-party library, React-Router
.
import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; export default function App() { return( <Router> <Link to="/">Home</Link> <Link to="/about">About</Link> <Link to="/users">Users</Link> <Switch> <Route path="/about" component={About} /> <Route path="/users" component={Users} /> <Route path="/" component={Home} /> </Switch> </Router> ) } // pages function Home() { return <h2>Home</h2> ; } function About() { return <h2>About</h2> ; } function Users() { return <h2>Users</h2> ; } ReactDOM.render(<App />, document.getElementById("root"));
Explanation:
From top to bottom, I’ll explain the code.
First, we are going to import our dependencies just like any normal React application, but here we’re also importing the React-Router
. Notice that we’re importing some named functions from the library: BrowserRouter as Router
, Switch
, Route
, and Link
.
The Router
is the container we’re going to use to hold everything that will be handled by our navigation. The Switch
is used to “switch” between different components based upon a matching route. Route
is the actual path with which the application is looking for and points to a matching component. Finally, we have a Link
, which is rendered like a standard HTML anchor link with a route attached to it.
Notice that in the Link
components, we have a to=
attribute. This is where we place the named route we would like to navigate to. Then, in the Route</code< components, each one has a
path=
attribute (which is the route to listen for) and a component=
attribute (which points to the component we want rendered on that path).
The rest of the code examples are simple components that make up our “pages” for our routing.
Remember, by default, React doesn’t have a built-in Router, which is why you must add an extra. There are many third-party libraries to add routing to your application and React-Router
is one of the more popular ones.
Let’s Talk to a Server Now
Now that we can navigate through our application, how can we actually get data to show in the pages?
Well, typically, we would make an AJAX call to a server via their API. AJAX, API? An asynchronous call to an application programming interface. This is a pretty common scenario for most web applications, so how can we do this in React?
React does not have a built-in mechanism for accessing API’s. But JavaScript does. In fact, the fetch
method was created and added to the language specifically for accessing remote resources, and it’s always available for your use.
Here is a simple example of what fetch looks like:
fetch('http://example.com/movies.json') .then((response) => { return response.json(); }) .then((myJson) => { console.log(myJson); });
Another common way for React applications to access remote server API endpoints is to use a third-party library called Axios. Axios is a promise-based HTTP client for the browser and Node.js. Here is an example of using Axios for accessing a remote server resource:
axios.get('http://example.com/movies.json) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }) .finally(function () { // always executed });
The largest difference between the two is that while Fetch
is built-in to the JavaScript language, Axios
is a third-party library. Otherwise, either one would be an excellent choice for accessing API’s inside of React.
Now Let’s Talk About State Management in React
State management refers to the management of the state of one or more user interface controls. This can be things like text fields, “OK” buttons, radio buttons, etc. in a graphical user interface. In this user interface programming technique, the state of one UI control depends on the state of other UI controls.
React At Its Simplest
React comes with several ways of setting state in components. One is through a class using the this.state
construct. The second is from the newer Hooks API of useState
where you can bypass using the this
keyword. To learn more about this, check out this React Hooks post on the Keyhole Dev Blog. Both of those are built-in features of React where we can manage the local state of a variable or control. The bigger issue comes about when you need to manage state on a much larger scale (i.e. an entire application).
While it is quite possible to use React’s local state management features, it could quickly become quite unwieldy to use.
What Are The Options for React Extras?
Newer versions of React now offer what we will call the React Context API. The React Context offers developers an easier way to pass data around without having to manually pass props down from function to function or class to class.
The React team says that it should be used for:
“…sharing data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language”
A third option is to fallback to several of the heavyweight champions of state management such as the popular Redux or Mobx third-party libraries. In general, both of these libraries basically provide an object with some methods which work as a unified place to hold your application’s entire state tree.
Let us take a look at a simple state management construct.
The following is a sample of using the builtin React Context API to enable passing around a user’s credentials inside of a React application.
First, we’re going to create a UserContext.js provider which will hold our user’s information.
import React from 'react' const UserContext = React.createContext({}) export const UserProvider = UserContext.Provider export const UserConsumer = UserContext.Consumer export default UserContext
Here we have created our User Context using the builtin React createContext
function. Then, we are exporting from this file both a UserProvider
and a UserConsumer
, and as the wording implies, one provides the context and one will consume the context.
Inside our App.js
file, we are going to pass in our provider so that it will be available globally to the rest of the application.
import React, { Component } from "react"; import { render } from "react-dom"; import HelloThere from "./HelloThere" import { UserProvider } from "./UserContext"; function App() { const user = { name: "Robert", loggedIn: true }; return ( <UserProvider value={user}> <HelloThere /> </UserProvider> ); } render(<App />, document.getElementById("root"));
Here we have imported our UserContext
and exposed the UserProvider
functionality to the application. We then have wrapped our children components (in this case the HelloThere
component) with the provider. And we have set a value=
on the provider. This value is now added to our global context. Notice that the component HelloThere
doesn’t have a prop
set for the user value. But by using the context
we can give it access. Let us take a look at that implementation.
import React, { useContext } from "react"; import UserContext from "./UserContext"; export default function HelloThere() { const user = useContext(UserContext); let loggedIn; if (user.loggedIn) { loggedIn = <span className="success">you are logged in</span>; } else { loggedIn = <span className="failure">you are not logged in</span>; } return ( <div> <h3>Hello {user.name} {loggedIn}</h3> </div> ); }
Initially, this component looks like any other React component, but notice that in addition to just importing React, we are also importing a Hook
called useContext
. This is going to allow us to programmatically access the state that we set in the App.js
file earlier. Secondly, we’re importing the actual UserContext
into this file.
To use the context via the hook, we create a user
variable and assign it to the value returned from the useContext
hook. We must pass in the context to the hook for this to work. Now, later in the code, we can access the user
object just like we would any other variable in our code, yet these values came from a global state rather than a local state.
While this was a simple example, you can see how easy it is to pass around data globally – without the need for passing props from one component to another. This greatly simplifies our code in the process.
Wrap Up
And that’s what we have for React extras this time around. We quickly touched on navigation within a React application, accessing server API endpoints for getting and saving data, and using context to create a global state for the application. When placed together, you have a pretty advanced application!