ReactJS Foundations Blog and Errata
React Router v6 Code Posted
Chapter 12 covers how to do Routing in React using React Router v5. In the time since the book came out, React Router v6 has been released, which includes some changes to how it’s used.
I’m also working on writing a version of Chapter 12 that covers React Router 6. I’ll post that to the book’s website when it’s finished.
All the code in the book still works, as long as you use React Router v5. However, if you’d like see how the examples in Chapter 12 are written in the latest version of React Router, I’ve converted them and put them in the repository here:
https://github.com/chrisminnick/react-js-foundations/tree/main/book-code-listings
As always, let me know if you have any questions!
Errata: Chapter 6
The final code example in the Shallow Copies and the Spread Operator section on page 160 shows a rest parameter with only two periods. There should be three. The correct code is:
function add(...numbers){
return numbers.reduce(sum,next) => sum + next);
}
React 18 Update
The biggest change in React 18 is the addition of concurrent rendering. With concurrent rendering, you can mark state updates as non-urgent in order to improve the performance of your user interface.
Concurrent rendering is opt-in. What this means is that it’s only enabled when you use a concurrent feature. As a result, apps built with previous versions of React (and every listing in my book) will work with React 18 with no changes.
If you want to start learning to use new concurrent features in React, you’ll need to make a couple changes to the file that renders your root component to replace ReactDOM.render()
with ReactDOM.createRoot().render()
.
Here’s an example of a typical index.js file in a React 17 app.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const root = document.getElementById('root');
ReactDOM.render(<App />, root);
This code will still work in React 18 with no changes. If you want to use new features of React 18, you’ll need to upgrade it. The steps to upgrade to React 18 are as follows:
- Open package.json and find the dependencies array. Change the versions for react and react-dom to the following:
"react": "^18.0.0",
"react-dom": "^18.0.0", - Open a terminal window and navigate to the root of your project and enter the following:
npm install - Open the file that calls ReactDOM.render (it’s probably index.js) and add /client to the path you’re importing it from. So, if you had:
import ReactDOM from 'react-dom/';
it will now be
import ReactDOM from 'react-dom/client'; - Change ReactDOM.render to ReactDOM.createRoot.render. Basically, instead of passing two parameters to ReactDOM.render, you’re now going to pass only the root component to the render method, and the location where you want to render it to the createRoot method.
So, if you previously had this:
ReactDOM.render(<App />, root);
You’ll now have this:
ReactDOM.createRoot(root).render(<App />);
Your final, upgraded, index.js should look like this:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = document.getElementById('root');
ReactDOM.createRoot(root).render(<App />);
Run your app and make sure everything still works. Congratulations!
In the coming weeks, I’ll post additional updates explaining how to use the new features of React 18.
Errata: Listing 4-12
In listings 4-12 and 4-13, the button’s onClick calls this.incrementCount and passed count+1 to it. There’s no need to pass a value here, since the incrementCount function increments the count variable.
The corrected / improved code for listing 4-12 should be:
import {Component} from 'react';
class ClassComponentState extends Component {
constructor(props){
super(props);
this.state = {count: 0};
this.incrementCount = this.incrementCount.bind(this);
}
incrementCount(){
this.setState({count: this.state.count + 1});
}
render(){
return (
<div>
<p>The current count is: {this.state.count}.</p>
<button onClick = {()=>{this.incrementCount()}}>
Add 1
</button>
</div>
);
}
}
export default ClassComponentState;
React 18 is Here!
The latest version of React has been a long time coming. This new version has some great behind-the-scenes updates to how React rendering works, makes changes to server-side rendering of React, and introduces the new Suspense feature, which lets you specify a loading state for components that aren’t ready to be displayed yet (such as ones that rely on the results of an asynchronous request).
I just upgraded reactjsfoundations.com to React 18, and I’m excited to announce that it didn’t break anything at all! This is huge. It’s extremely rare that a major version update of a library doesn’t wreck everything. It’s even more rare that a major version update that happens right after a book is published doesn’t require major updates to the book.
In this case, however, all of the code in the book will continue to work just fine, whether you’re using React 17 or React 18. Furthermore, almost all of the content of the book is relevant to both React 17 and React 18. There may be a few places where more content on certain subjects is needed (such as Suspense, which is covered in the book but not extensively). I’m working on that.
I’ll be posting React 18 tutorials and info here over the next couple weeks, so make sure to check back regularly. If I find anything in the book that should be changed for React 18, I’ll also post it here. However, so far I’m seeing only positives from React 18. The ReactJS team did an amazing job of making major changes to the library without breaking everything that was written with the previous version. That’s extremely hard to do, and the ReactJS development team should be commended and praised for their dedication to taking the time to get it right.
Writing an Error Boundary
Errors can happen in React components for many reasons. In development mode, errors will cause React to spit up a bunch of red text into the browser. This is sometimes helpful for debugging the problem.
In production mode, React will display a white screen (called the “white screen of death”) rather than report to the user what caused the error. This makes a lot of sense, but it will likely result in the user getting confused and/or leaving your app.
Error boundaries are a way to catch errors in React components before the white screen of death appears and to display something more friendly. You can even use an error boundary to provide a “try again” button and to log the error to a remote logging service.
Error boundaries are created using the getDerivedStateFromError lifecycle method and, optionally, the componentDidCatch lifecycle method. Because they depend on lifecycle methods, error boundaries must be written as class components.
The getDerivedStateFromError lifecycle method runs when a child of a component returns an error. It accepts the error as an argument and returns an object that will be used to update the state. Here’s an example of an error boundary:
import {Component} from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>There has been an error.</h1>;
}
return this.props.children;
}
}
export default ErrorBoundary;
To use this error boundary, import it into a component that has a child that may return an error. Then, wrap any component with the ErrorBoundary JSX element.
import ErrorBoundary from './ErrorBoundary';
function App(props){
return (
<ErrorBoundary>
<ComponentThatMightError />
</Errorboundary>
)
}
export default App;
If a component wrapped in the error boundary returns an error, the error boundary will catch the error and display the “There has been an error” text. If you want to give the user a chance to reset the state and try again you can also return a button that will reset the hasError state property to false, which will cause the subcomponents of the error boundary to re-render.
To learn more about error boundaries, see chapter 13 of ReactJS Foundations. You can view additional examples of Error Boundaries at reactjsfoundations.com.
Prop Drilling
Prop drilling is a normal pattern and best practice in React. There are times, however, when ‘global’ data, such as user preferences or theming information must be passed to many different component and through multiple layers.
Conditional Rendering with Element Variables
The benefit of using this method for conditional rendering is that it’s easy to read, and you can have as many branches as you need.
Conditional Rendering with &&
Using the &&
operator is a quick and easy way to conditionally render a component. Unlike using an if/else
statement, a switch
statement, or the ternary operator, using &&
doesn’t provide a way to choose between 2 or more components to render.
Errata
No matter how many times a book is checked and how many editors go over it, it’s inevitable that there will be typos or bugs. Some of these bugs will be due to changes that happened in React or any of its dependencies since the book was written, and some may even be things that we missed during writing and production of the book (gasp, I hope not!).
I’ll post updates to the book as well as corrections for any incorrect information or typos here. If you find a typo or something that’s not working as expected, please submit a comment here.
Download the examples, report issues, and ask/answer questions in the discussion area by visiting the book's github page. All of the code for the book is also available on codesandbox.io for you to play around with.
ReactJS Foundations is published by John Wiley and Sons, Inc and is available in paperback and eBook.