Skip to content

Instantly share code, notes, and snippets.

@neosaurrrus
Created September 24, 2018 06:23
Embed
What would you like to do?

This is part 8 of my React Learning series. Using knowledge gleaned from Wes Bos' React for Beginners. Last time we:

  • Built a component to display data
  • In the containing component, mapped through the array (Object.Keys if its an object) to make multiple components
  • Gave each component a unique key.
  • Passed in State data via props
  • Populate the component's render method with whatever data you want to give it.

Let's look again at how we add things to state, this time via a onClick event.

The setup

This time we are using the example of a shopping basket, with a number of grocery items that has be added to it. We have three specific components involved:

  1. Grocery - A div for each item, Where we also have an 'Add' button for each item
  2. Order - Where we can see what we have ordered with total for that order.
  3. App - The parent component with a state that contains two objects: - grocery which displays a specific item on sale - order which will contain the order for the user. Currently blank.

So the general idea is to:

  1. Create a function to add the associated grocery to the order, multiple times if needed.
  2. Display the order on the Order component, totting up the prices as required.
  3. On the Groceries component, configure a button with an event method to handle the click to add to the order.

Making an Add to Order function

As we are updating the state on the app component, we want to have the method here as well. The method must do three things:

  1. Take a copy of the relevent part of state
  2. In this copy, add a grocery to the order or increment the current grocery by 1;
  3. Update the state

Taking a copy of state

This just relies on using the spread operator:

const order = {...this.state.order}

Add to Order or Increment By 1

This uses a little trick where the OR statement runs the first condition if the item exists or else makes it exist by adding a 1.

order[key] = order[key] +1 || 1;

Use setState to Update the State

this.setState({ order })

Note that order is used to update order in the State, thats order:order which shortens in ES6 to order

Here is the method in it's entirety:

https://gist.github.com/02b9ab88a62599355eedcc49f5fdb035

Passing the Method and Key to the Button's Component

The button we want to add lives in the grocery component which shows details about the particular grocery (name, price.. etc)

As app is the parent of the grocery component we need to first pass the method into props: addToOrder={this.addToOrder}

Now we have a slight quandry, the method needs the object key passed into it. As the component is created without visabiity of its own key, how do we get that? The answer is that we need to pass it through as a prop as well...

index={key}

Setting the Event on the Button

First lets create the event on the button: <button onClick={this.handleClick}>

Second, lets create that handleClick function property on the component:

handleClick = () => {this.props.addToOrder(this.props.index)}

Some people might like to put the function on the method inline on the button, but I like seperating it out for clarity.

Cool, if everything has gone to plan we should be able to see items being added to the App state when we click that button. Now for the other side of the equation, displaying what we have just placed into state.

Displaying the State object on our Order form

Now that we have the state getting updated when we hit our button, we probably should show the result of that somewhere. Much of this going to involve dealing with edge cases that may or not apply in your scenario. Essentially we need to pass in the relevent prop and display the data as we loop through each key.

Our order form in this example, needs to show the following things:

  1. The item(s) we have ordered
  2. How many of each item we have ordered
  3. The price of that item mutiplied by the amount.
  4. The whole total.
  5. Check if the grocery item becomes sold out and react accordingly.

Getting the Props

So before we do anything else, in App.js we need a prop to pull in the groceries and order objects:

  • The groceries object contains the name, price and status of each grocery
  • The order object contains how many of each item has been ordered.

We need both to figure out things so pull them into the Order component like so:

<Order groceries={this.state.groceries} order={this.state.order}/>

You might be tempted to add all of the state as thats the only objects in it, but in the sprit of modularity, we cannot be sure that will always be the case so its good practise to be explicit in what you are taking as props.

Working with the Prop Data

In the Order.js file we can build some variables we could use:

An array of Object Keys const orderIds = Object.keys(this.props.order) - gives us an array of each key in the order object. If we display that using {orderIds} we should each order item appear.

A total of all items We have to check a few things as we figure out the total via the Reduce array helper method:

https://gist.github.com/b46c8bdae4439111253f57109e9e774c

Remember to actually display, and style the total somewhere: {total}

Loop over the OrderIds

  1. In the JSX we are returning, make a
      to contain the OrderIds we will display.
    • In the UL, map over the orderIds to return an list item for each item ordered: OrderIds.map(key => <li>{key} </li>)

The JSX will look something like this:

https://gist.github.com/ea9128645cfd7f37b99c99a309e240d9

Render Functions

By now your render function is starting to get a little long, which is a sign too much is going on in your component. We could make a component for the code but if its not quite big enough we can shunt some of the code to a function inside the component, removing it from render

For example we can take the logic for each list item above and put it into a function on the component like so:

https://gist.github.com/a54b9490055e549a28db747bf4474f8c

Now the unordered list can look like this: <ul>{orderIds.map(this.renderOrder)}</ul>

It's a fairly basic example but its something to bear in mind as a middle ground between putting everythign in render and making another component.

Presumably you will want to show more than just the key for each order, but that's a detail you can figure out for your own needs! Some extra details may include:

  • The grocery name
  • The count of orders for that grocery
  • The price of (grocery.price * count)
  • Check if the grocery is still availible and change the list item should it become unavailible during the order.

Once you start adding that complexity, using a Render method starts making more sense.

Add keys to each List item

As we have created multiple similar list items, React will get a little upset and warn us about the lack of unique keys for the list items.

This is fairly straightforward to sort since our Grocery Object keys are unique:

<li key={key}> {key} </li>

Conclusion

As long as we set up the props and manage the amount of code in our render function there is little React specific knowledge required for displaying Sate data. Having a good grasp of JS though will make life easier when it comes to getting the right bit of data from our props and displaying it effeciently. the rest is up to what exactly you want to display with your State data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment