Day 1, Segment 5

In notebook:
FrontEndMasters React Intro 2
Created at:
2016-12-24
Updated:
2017-01-09
Tags:
libraries React JavaScript jQuery
Branch v2-10

Shape in Proptypes

You can use ​shape()​ :
  
const { shape, string, bool } = React.PropTypes

const ShowCard = React.createClass({
  propTypes: {
    show: shape({
      poster: string,
      title: string,
      year: string,
      description: string
    })
  },
  render () {
    const { poster, title, year, description } = this.props
    return (...)
  }
})
You describe the object you're going to use. If you know what your object is going to look like, then it's a good idea to use shape to describe it. Similar to ​array of​.

You can nest shapes in shape.

Shorten the JSON data declaration with the spread operator

Use the spread the operator (​...​) in JSX. This is JSX, but mimics ES6. 
  ...
import preload from '../public/data.json'

const Search = React.createClass({
  render () {
    return (
      <div className='search'>
        {preload.shows.map((show) => {
          return (
          // 1. ++++
            <ShowCard key={show.imdbID} {...show} />
          )
        })}
      </div>
    )
  }
})
...
The above is same as
  <ShowCard key={show.imdbID} {...show} />

// same as

<ShowCard key={show.imdbID} poster={show.poster} title={show.title} />
Usually explicit is better, but sometimes can be useful. The spread will arrive in ES2017.

Update component proptype after the spread

Now, with spread operation above, the child component ShowCard receives not one object proptype, but as a list. Let's update this component:
  // ****     ShowCard.js   ****

import React from 'react'
// 3. ++++
const { string } = React.PropTypes

const ShowCard = React.createClass({
  // 2. ++-- can remove shape
  propTypes: {
    poster: string,
    title: string,
    year: string,
    description: string
  },
  render () {
    // 1. ++-- this will be just this.props
    // and not this.props.show
    // const { poster, title, year, description } = this.props.show
    const { poster, title, year, description } = this.props
    return (
      <div className='show-card'>
        <img src={`/public/img/posters/${poster}`} />
        <div>
          <h3>{title}</h3>
          <h4>({year})</h4>
          <p>{description}</p>
        </div>
      </div>
    )
  }
})

export default ShowCard
This is a better, cleaner pattern as opposed to passing the ​.show​ object. Less nesting is almost always better when it comes to proptypes. It's simple to know what proptypes you need to pass to the component.

Setting default values to proptype

​getDefaultProps​. But if you're using it too often, you're probably doing it wrong. Later, ​state​ will explain why...

React state

One way data-flow

"State is the enemy of all apps
If you have a bug it's always coming from the state.

React tries to minimise this problem, by reducing the number of places you can modify state. This resonates with the idea that if you find a bug in your app, you know the one place, where to look (to debug it).

There's no sideways modification of state in React. If you modify state, it's in the component itself, nowhere else.

Branch v2-10

(insider joke about Henriks yolo in his FEM workshop...)

09:00

For example: Search component has a child ShowCard. ShowCard cannot modify Search's state. So if you have a bug, it can only come from Search itself.
Notice that the sub-component doesn't, and cannot pass anything (proptypes, etc) up, to the parent component. This is called one-way data flow. React pioneered it, now Ember does it too. Data always flows down.

How do you move data up then?

11:20

You take the data out from the sub-component and put it in the parent component and pass it down again. If two components share the same data, then the common ancestor is where that data lives. 

Will show a concrete example later. 

What if your component has a button, and it has to affect a parent element?
Notify the parent with a callback function
We pass a (callback) function to the child that it can modify when its state changes and then the parent component will modify itself its own state. 

Make React keep track of state

The state will be the search term.
  // ****   Search.js

import React from 'react'
import ShowCard from './ShowCard'
import preload from '../public/data.json'

const Search = React.createClass({
  // 2.++++ store the state here
  // (ES6 enhanced object literal syntax)
  getInitialState () {
    return {
      searchTerm: ''
    }
  },
  // 3. ++++ React synthetic event
  handleSearchTermChange (event) {
    this.setState({searchTerm: event.target.value})
  },
  render () {
    return (
      <div className='search'>
        <header>
        // 1.++++
          <h1>svideo</h1>
          // value={this.state.searchTerm}
          // the input field value will auto-update
          <input onChange={this.handleSearchTermChange} value={this.state.searchTerm} type='text' placeholder='Search' />
        </header>
        <div>
          {preload.shows
          // 4. ++++ filter (explained much later below...)
            .filter((show) => `${show.title} ${show.description}`.toUpperCase().indexOf(this.state.searchTerm.toUpperCase()) >= 0)
            .map((show) => {
              return (
                <ShowCard key={show.imdbID} {...show} />
              )
            })}
        </div>
      </div>
    )
  }
})

export default Search
By design, there's no two-way data binding in React. So above, <input value={this.state.searchTerm}​ will not auto update yet because nothing modifies yet this.state.searchTerm​. React does listen to and captures all keyboard events. 

(Debugging two-way databinding (Angular) is very hard, too much magic there...)

So we need a function to update this.state.searchTerm​ when typing: ​onChange={this.handleSearchTermChange}​ 

React synthetic events

React uses synthetic events, that is it captures the events first and you use the  synthetic event passed to you by React. 

​event.target.value​ is what you would get with the original event.
    handleSearchTermChange (event) {
    this.setState({searchTerm: event.target.value})
  },

​setState

This is the only way in React to modify the state. You only modify the delta ie. the only part of the data you're changing. Behind the scenes, React will do an ​Object.assign​ to merge it into complete state data. 
It doesn't do a deep merge, so if you have a deeply nested data, you have to handle it yourself. 

And now, as the state has been updated, it will kick off a re-render. 

​forceUpdate

If you modify the state not using ​setState​, then it will not do a re-render. In this case you need to do ​this.forceUpdate()​.
This is not efficient. 
With ​setState​, React will batch the rerenders. It's an async function and will queue the update. 
So, never use ​forceUpdate​!

Except, if you need to integrate it with D3 (or another library), which has to be in control. 

VirtualDOM

It's just an implementation detail in React. "It does not make React desirable, only feasible".
What matters is that it works and it's fast. You don't need to know how it works (for debugging).

State over time (because of spaghetti code)

State over time is what makes app difficult to debug. This is a particularity of spaghetti code, you end up writing with jQuery. State builds up over time, as you add more and more event handlers.

React eliminates the time component 

Given a state, which can be thought of as a snapshot, the component always behaves the same way. 

Filtering the list (search)

From above...
Use JavaScript Array.filter​ and ​String.indexOf​ for string matching. And ​String.toUpperCase​ to ignore case. 
Not very smart, but OK for the demo...
  {preload.shows
// 4. ++++ filter (explained much later below...)
  .filter((show) => `${show.title} ${show.description}`.toUpperCase().indexOf(this.state.searchTerm.toUpperCase()) >= 0)
  .map((show) => {
    return (
      <ShowCard key={show.imdbID} {...show} />
    )
  })}