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-app
Go to the project directory and install the dependencies.
cd react-first-app
npm install
npm start
Starts 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 build
Builds 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 test
Runs 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!