This article originally appeared on stef.ninja.
This is the third post in a series of notes from the React for Beginners course from Wes Bos that I am working through. This is an AWESOME course and I highly recommend it to anyone looking to learn React.
You can find more notes in Week 1 and Week 2 of the series.
Day 15: Display Data from the State
We have the sample fish data saved in our state. Let's get these fish displaying in the UI.
JSX doesn't have any loops or logic built in so to combat this we can use regular javascript (by encapsulating it in {}). To display a list of our sample fish we will map over the entire list of keys within the fish
object like so:
<ul class="list-of-fishes">
{
Object
.keys(this.state.fishes)
.map(key => <Fish key={key} />)
}
</ul>
Wes gives a good tip around ES6 destructuring that means when we are referencing the props that are sent down to the child we don't have to repeat ourselves with this.props
all of the time.
const {details} = this.props;
Further reading: Wes Bos has A Dead Simple intro to Destructuring JavaScript Objects
To Do:
- Pass the details about each fish down to the Fish component via the
details
prop. Hint:details={this.state.fishes[key]}
- Use the formatPrice function in the helper file to format the Price detail.
- Make a component (Fish.js) that displays the data for each fish. Return this all in a
<li>
. - Iterate through a list of the sample fish in the fish object & display the Fish component.
Today the app looks like:
[Video 15: Displaying State with JSX]
Day 16: Updating State
Today we're linking up the Add To Order
button so it actually adds the corresponding fish to the state.
The button has an awesome feature where it dynamically changes depending on whether a fish is available or not. To do this we will be implementing some logic around details.status
using a ternary operator.
const isAvailable = details.status === 'available';
const buttonText = isAvailable ? 'Add To Order' : 'Sold Out!';
To Do:
- Change the content of our
Add To Order
button to be dynamic depending on thedetails.status
. - Change the button to be disabled if the fish is unavailable.
- Add
addToOrder
method on the app component. //Take a copy of the state //Update the new number of fish ordered e.gorder[key] = order[key] + 1 || 1;
//Update state. - Bind the method to the app component.
- Pass the
addToOrder
down to our child via props. - Add an
onClick
handler to the button that updates the state of the order. Note: To do this we will need a reference to the key. Pass this down as a prop on the Fish component.
Quick Note: I send these notes over to my best dev friend Mark to read through before publishing & he always gives me great notes on things that I'm not 100% across. Below are his notes on the function that we used to update the number of fish ordered:
If the
key
isn't set inorder
thenorder[key]
will returnundefined
, which meansorder[key] + 1
=>undefined + 1
=>NaN
, and becauseNaN
is falsey the1
in theNaN || 1
expression is returned. It is small but important to understand the sequence of transforms that the data goes through so you don't get tripped up later on.
Today the app looks like:
It looks exactly the same as yesterday because we didn't make any UI changes.
[Video 16: Updating Order State]
Day 17: Display State via JSX
In today's lesson we are taking the order
state that we have been working with and injecting it into the UI via JSX.
This is the first time that we are calling another method within the render method to handle a fair bit of our JSX output. Wes mentions that this can be done using another component but he has opted to an additional method within the Order.js component to keep it simpler (no need to hand down props once we bind it).
To Do:
Todays Goal: Display fish order via Order.js
. This component needs to include the number of lbs, name of fish, total cost of each line item, and total cost. We will do this by:
- Passing the
fishes
&order
state to the order component via props. - Create a running total price of all of the fish included in the order.
- Importing and using the
formatPrice()
function from the helpers file. - Creating a
renderOrder()
function within the Order component to handle the JSX returned for each fish within the order. This can be called from within therender()
function like this:{orderIds.map(this.renderOrder)}
- Bind our custom method to this via a constructor.
- Remember to add the key to all list items to ensure that React can reference them.
Today the app looks like:
[Video 17: Displaying Order State with JSX]
Day 18:
Persiting data requires a backend service. In this lesson we use Firebase from Google as our backend service.
It uses HTML5 Websockets which Wes was pretty excited about but I had to do some research around. This video was a really good explanation of it with an example: Introduction to Websockets and Firebase - How to Build Real-Time Apps
So with Firebase we have a realtime backend database. A great feature of the Firebase Realtime Database is that it is basically a giant object. Which syncs perfectly with React State also being an object.
During this lesson we also explored the React Lifecycle Hooks. These lifecycle methods offer us different entry points into a component.
We focussed on the componentWillMount method that is invoked before the initial component is rendered. This is a great time to sync the database object with our state object. It also has a coupled componentWillUnmount method that allows us to break the connection.
To Do:
- Sign up for a Firebase account.
- Create a new Project in Firebase.
- Edit the Realtime Database rules to be less secure (don't worry we will fix this us in the authentication lesson set for day 24). Set the Realtime Database rules to:
{
"rules": {
".read": true,
".write": true
}
}
- Use the re-base package to implement Firebase into the React app with the following being entered into
base.js
:
import Rebase from 're-base';
//Get some information from your Firebase project
const base = Rebase.createClass({
apiKey: YOUR API KEY,
authDomain: YOUR AUTH DOMAIN,
databaseURL: YOUR DATABASE URL,
});
export default base;
- import
base
into the App - Hook into the
componentWillMount()
lifecycle method to sync the App state with the Firebase Realtime database.
Today the app looks like:
[Video 18: Persisting our State with Firebase]
Day 19: Persisting State with localstorage
In todays lesson we explored persisting the state of our order via HTML5 Local Storage.
Local Storage allows us to store the data associated with the order within the browser. It is a good option for the order to be stored in because it is secure, local and doesn't impact on website performance.
Local Storage uses key value pairs. The value only accepts numbers, strings or booleans. This means we need to transform our order object into a string. We can do this via the JSON.stringify()
method (you can always turn it back with JSON.parse()
).
We were also using the React Lifecycle Methods in this lesson. Specifically the componentWillUpdate
method that is invoked immediately before rendering when new props or state are being received. This method also isn't called for the initial render which suits this data object (order) well.
The Lifecycle methods on <App>
currently look like:
To Do:
- Pass down all of the params to the
<Order>
component. - Hook into the
componentWillUpdate
method and set the localStorage to ourorder
state. - Hook into the
componentWillMount
method (already called) and check whether there is any order in localStorage. - If there is an order saved in localStorage then update the order state.
Today the app looks like:
[Video 19: Persisting Order State with localstorage]
Day 20: Live State Editing with Bi-directional State
Today was a pretty involved lesson. We managed to set up state editing via the inventory management from the UI of the app.
First we needed to create a form for each of the fish objects within our state. This was done in the Inventory
component via a new renderInventory()
method we created:
Once the form had been created we needed a new method to handle the updates. This was done with the handleChange
method that we created:
The final method we added was on the App component and it made the magic happen. The state is only updated via the App component so this is why everything had to be passed up there.
This pattern of copy the current state, overwrite the section that has changed, then update the state is becoming a go to when writing React code.
Note from Mark:
Passing each of these functions inline is a great opportunity to use memoization. Currently, with every change that is put through any of those inputs all children are re-rendered. This isn't a giant performance suck at this level as they are only inputs (not large components) but it is good to get into the habit of memoization early.
We used lodash.memoize. This is the improved functions that we worked out:
Further reading on this:
Lodash memoize
The Big O Notation
To Do:
- Loop over all of the fish within the state and output an inventory management form (much like the new fish form)
- Pass
fishes
down to<Inventory />
via props. - Create
renderInventory()
method that will handle all of the form rendering. - Add the current values of the fish (from state) into each section.
- Data bind the values so each time an edit is made this is reflected in the state (and on the Firebase database) (remember to use
defaultValue
instead ofvalue
so that the field is mutable). UseonChange{(e) => this.handleChange(e, key)}
. - Create a custom
handleChange()
method and bind it using the constructor. Within that method take a copy of the fish state. Then use the computed target to update ONLY the element that has changed:
handleChange(e, key) {
const fish = this.props.fishes[key];
//Copy the fish and update it with new data
const updatedFish = {
...fish,
[e.target.name]: e.target.value
}
this.props.updateFish(key, updateFish);
}
- Pass the updated fish up to the
<App />
component. - Create a custom method to set the state of the fishes to the updated fish (use
updateFish()
). - Make the
updateFish()
available to the<Inventory />
Today the app looks like:
[Video 20: Bi-directional Data Flow and Live State Editing]
Day 21: Deleting Items from State
CRUD (or Create, Read, Update and Delete) is the basic functions required for persistent storage. We have already worked through creating objects to save in the state, reading the state and updating the state. In today's lesson we focus on deleting objects from state.
To do this we run through a similar pattern to the one I highlighted yesterday:
removeFish(key) {
//Copy the current state
const fishes = { ... this.state.fishes };
//Delete the specific fish
//Note: 'null' needs to be used because of Firebase
fishes[key] = null;
//Set the State
this.setState({ fishes });
}
That's it. The fish is now deleted. Obliterated from existence.
To Do:
- Create a
removeFish
method that deletes a specific fish from the state. - Create a
Delete Fish
button within therenderInventory
JSX - Link the
onClick
handler up to theremoveFish
method.
Today the app looks like:
[Video 21: Removing Items from State]
Read more in this series: Week 1, Week 2, and Week 4 Or get the React for Beginners Course for yourself.
Top comments (0)