SPAs or Single Page Applications, are applications that will not download the html markup from the server on every new link click. Instead, the application will use a component named Link
that prevents link clicks from triggering a header location change.
Routing
Unlike with other frameworks like Angular, in React we do not have the concept of routing, because React is a simple lightweight library, and not a complete framework, it is only responsible for rendering the view; nothing more. So to add routing to our application, we need to install a library called react-router-dom
.
How to make a component available from every other one
In index.js
:
//...
import { BrowserRouter } from 'react-router-dom'
//...
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root');
);
registerServiceWorker();
By wrapping our main <App />
component with <BrowserRouter></BrowserRouter>
, we are allowing BrowserRouter
to pass properties to the App
component (which is a child).
The Browser History object
BrowserRouter
is a component wrapping the browser's history
object. Which you normally access through:
window.history.back();
//or
window.history.go(-1);
window.history.forward();
//or
window.history.go(1);
// or to refresh the page:
window.history.go();
window.history.pushState({foo: 'bar'})
//etc.
Workflow of react-route-dom
To make use of react-route-dom
package, we need to first wrap the App component with BrowserRouter in index.js
. This could have been done in App.js but it makes more sense to return an App
component in the App.js file instead of a wrapped app? This has the effect of making the history
object available from everywhere down the App and children components.
Then we need to control what component is displayed based on the current url in the browser. This is done using routes. From App.js:
import {Route} from 'react-router-dom';
//...
class App extends Component {
render() {
return (
<div>
<Route path="/products" component={Products} />
</div>
);
}
}
The Route
component looks at the current url, and if the path matches, then it will render the specified component. It is like a simple if Statement. Note that if we put many routes, if many routs match, then every matching component will be rendered. You could limit this with the exact
keyword
import {Route, Switch} from 'react-router-dom';
//...
class App extends Component {
render() {
return (
<div>
<Switch>
<Route path="/products" component={Products} />
<Route path="/cart" component={Cart} />
<Route path="/" component={Home} />
</Switch>
</div>
);
}
}
The Switch
component allows creating if elseif, ..., elseif, else
expressions. That is why you should order routes from most specific to most general.
Then we need to give the user a way to go to the specified route. This is done through links in the application. We will thus create a Navigation
component, containing all the links to the different routes in the App component.
2019 Additions to react
React Hooks
2020 additions to react
Concurrent mode
It allows the framework to handle multiple state updates at the same time.
It opens the door to a new builtin react component called Suspense
. Suspense will wait for asynchronous activity to finish and its children, such as an api call to a database, or better yet, using the new dynamic imports feature to lazy load a react component at runtime:
import React, { Suspense } from 'react';
React.lazy(() => import('./UserProfile');
function App() {
return (
<Suspense fallback={ <Spinner /> }>
<UserProfile />
</Suspense>
);
}
That means you can handle loading states declaratively and you don't need to manually toggle some kind of boolean value in your code.