Learning React - Week 3

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:

App on Day 15 of the React for Beginners Course

[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 the details.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.g order[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 in order then order[key] will return undefined, which means order[key] + 1 => undefined + 1 => NaN, and because NaN is falsey the 1 in the NaN || 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:

App on Day 16 of the React for Beginners Course

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 the render() 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:

App on Day 17 of the React for Beginners Course

[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.

Code in App.js used to connect to the database via re-base

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:

App on Day 18 of the React for Beginners Course

[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:
Lifecycle methods

To Do:
  • Pass down all of the params to the <Order> component.
  • Hook into the componentWillUpdate method and set the localStorage to our order 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:

App on Day 19 of the React for Beginners Course

[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:

renderInventory method that we created today

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:

handleChange method that we created today

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.

updateFish method that we created on the App component today


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:

Improved function performance using memoize

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 of value so that the field is mutable). Use onChange{(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:

App on Day 20 of the React for Beginners Course

[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 the renderInventory JSX
  • Link the onClick handler up to the removeFish method.
Today the app looks like:

App on Day 21 of the React for Beginners Course

[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.