This workshop covers the fundamentals of React.
This app called "Catch of the Day is an real-time app for a trendy seafood market where price and quantity available are variable and can change at a moment's notice.
This application is from a great course called React For Beginners by Wes Bos. Please have a look at his training courses here.
git clone https://github.com/jcdesousa/react-first-app.git react-first-appGo to the project directory and install the dependencies.
cd react-first-app
npm installnpm startStarts the app in development mode, watches the source files, reloads the page if changes are made to the code and shows build errors and lint warnings in the console.
Open http://localhost:3000 to view it in the browser.
npm buildBuilds the app for production to the build folder. It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes. By default, it also includes a service worker so that your app loads from local cache on future visits.
Your app is ready to be deployed.
npm testRuns the test watcher in an interactive mode.
By default, runs tests related to files changed since the last commit.
When the form is submitted, we need to get the text from the input box and transition from the url / to /store/:storeId.
Open StorePicker and change the method goToStore.
// src/components/StorePicker.js
goToStore = (event) => {
event.preventDefault();
const storeId = this.storeInput.value;
this.props.history.push(`/store/${storeId}`);
}The ref attribute takes a callback function, and the callback will be executed immediately after the component is mounted or unmounted. Using the ref callback just to set a property on the class is a common pattern for accessing DOM elements.
We need to listen for an submit event on the form and call goToStore when it.
<!-- src/components/StorePicker.js -->
<form className="store-selector" onSubmit={this.goToStore}>We need to import the App component and create a parameterized route in index.js that matches the path pattern to render the component.
// src/index.js
import App from './components/App';// src/index.js
<Route path="/store/:storeId" component={App} />// src/components/Fish.js
import React from 'react';
import { formatPrice } from '../helpers';
class Fish extends React.Component {
render() {
const { details, index } = this.props;
const isAvailable = details.status === 'available';
const buttonText = isAvailable ? 'Add To Order' : 'Sold Out!';
return (
<li className="menu-fish">
<img src={details.image} alt={details.name} />
<h3 className="fish-name">
{details.name}
<span className="price">{formatPrice(details.price)}</span>
</h3>
<p>{details.desc}</p>
<button onClick={() => this.props.addToOrder(index)} disabled={!isAvailable}>{buttonText}</button>
</li>
)
}
}
export default Fish;In our App component, we need to show all the fishes in our store. On the render method, add an list of Fish components. Import the Fish component.
// src/components/App.js
import Fish from './Fish';To render multiple items in React, we pass an array of React elements. The most common way to build that array is to map over your array of data. Let’s do that in the render method of App:
// src/components/App.js
<ul className="list-of-fishes">
{
Object.keys(this.state.fishes)
.map(key => (
<Fish key={key} index={key} details={this.state.fishes[key]} addToOrder={this.addToOrder} />
))
}
</ul>key is a special property that’s reserved by React. When an element is created, React pulls off the key property and stores the key directly on the returned element. React uses the key automatically while deciding which children to update; there is no way for a component to inquire about its own key.
React components can have state by setting this.state in the constructor, which should be considered private to the component.
In this aplication the state is stored in the App component and then passed to the children components via props.
Import the samplesFishes.
// src/components/App.js
import sampleFishes from '../sample-fishes';Create a method called loadSamples that updates the state with the samples fishes.
// src/components/App.js
loadSamples = () => {
this.setState({
fishes: sampleFishes
});
}Whenever this.setState is called, an update to the component is scheduled, causing React to merge in the passed state update and rerender the component along with its descendants.
When the component rerenders, this.state.fishes will be an array of the sample fishes so you’ll see it updated in the list.
We need to add a button to the inventory pannel to load the samples fishes when is clicked.
The usual pattern here is pass down a function from App to Inventory that gets called when the Load Samples Fishes button is clicked.
// src/components/App.js
<Inventory
addFish={this.addFish}
removeFish={this.removeFish}
loadSamples={this.loadSamples}
fishes={this.state.fishes}
updateFish={this.updateFish}
/>After the AddFishForm component inside of the render method of Inventory component, add a Load Sample Fishes button with a click event that invokes the method named loadSamples passed by the props.
// src/components/Inventory.js
<button onClick={this.props.loadSamples}>Load Sample Fishes</button>React supports a simpler syntax called functional components for component types like Header that only consist of a render method. Rather than define a class extending React.Component, simply write a function that takes props and returns what should be rendered.
Replace the whole Header class with this function:
// src/components/Header.js
import React from 'react';
const Header = (props) => {
return (
<header className="top">
<h1>
Catch
<span className="ofThe">
<span className="of">of</span>
<span className="the">the</span>
</span>
Day
</h1>
<h3 className="tagline"><span>{props.tagline}</span></h3>
</header>
)
}
export default Header;You’ll need to change this.props.tagline to props.tagline. Many components in your apps will be able to be written as functional components these components tend to be easier to write and React will optimize them more in the future.
As your app grows, you can catch a lot of bugs with typechecking. For some applications, you can use JavaScript extensions like Flow or TypeScript to typecheck your whole application. But even if you don’t use those, React has some built-in typechecking abilities. To run typechecking on the props for a component, you can assign the special propTypes property.
Import the PropTypes.
// src/components/Header.js
import PropTypes from 'prop-types';On the Header component set the prop tagline to type string and required
Header.propTypes = {
tagline: PropTypes.string.isRequired
};On the Inventory component set the prop loadSamples as a function and required
Inventory.propTypes = {
fishes: PropTypes.object.isRequired,
updateFish: PropTypes.func.isRequired,
removeFish: PropTypes.func.isRequired,
addFish: PropTypes.func.isRequired,
loadSamples: PropTypes.func.isRequired,
};You're at the end of your journey, and you've accomplished a lot. Congrats, You are awesome!