This is the curriculum for the 2018 Foxtrot Web Developer Bootcamp.

Higher Order Functions

Today we're going to look deeper at higher order functions when working with arrays, and even write some of our own functions. Then we'll turn our attention to React, and look closer at some of the higher order functions that we've already been using there to gain a better understanding of Javascript functions, and React.

Goals

1) slice/find/map
2) reduce
3) writing our own higher order functions
4) higher order functions in React

Array Functions

slice()

We've already seen a few examples of slice, and how we can use it in functional programming. Its a useful tool when we need to make a copy of an array, but there's more we can do with it.

Let's look first at cloning an array:

let famousProgrammers = [
  "Rich Hickey",
  "Robert Martin",
  "Grace Hopper",
  "Richard Stallman",
  "Dennis Ritchie"
]

let newList = famousProgrammers.slice()
console.log(newList)
/*
[ 'Rich Hickey',
'Robert Martin',
'Grace Hopper',
'Richard Stallman',
'Dennis Ritchie' ]
*/
// We can see that we've just made an exact copy


let shortList = famousProgrammers.slice(2)
console.log(shortList)
// [ 'Grace Hopper', 'Richard Stallman', 'Dennis Ritchie' ]
// This time, we start with the 3rd element in our array

let subList = famousProgrammers.slice(2,4)
console.log(subList)
//[ 'Grace Hopper', 'Richard Stallman' ]
// Here we grab from index 2 to index 4 for our new array

A word of warning about slice()

Slice makes a shallow copy of the array. This means that if you have an array of things that contain things, you will not make a full copy of the nested things. Consider the following:


let famousProgrammers = [
  "Rich Hickey",
  "Robert Martin",
  "Grace Hopper",
  "Richard Stallman",
  "Dennis Ritchie"
]

let famousGolfers = [
  "Jack Nickolas",
  "Tiger Woods",
  "Sam Snead"
]

let lists = [
  {name: 'programmers', list: famousProgrammers},
  {name: 'golfers', list: famousGolfers}
]

let newList = lists.slice()
newList[0].list.push("Linus Torvalds")
console.log(famousProgrammers)
/*
Uh ohh!  We modified the programmers list.  This will likely lead to bugs
[ 'Rich Hickey',
  'Robert Martin',
  'Grace Hopper',
  'Richard Stallman',
  'Dennis Ritchie',
  'Linus Torvalds' ]
  */

find()

Find does what you'd expect. It reduces an array to the first element that matches the callback function:

let famousProgrammers = [
  {name: "Rich Hickey", language: 'Closure'},
  {name:"Robert Martin", language: 'Java'},
  {name: "Grace Hopper", language: 'Cobol'},
  {name: "Richard Stallman", language: 'lisp'},
  {name: "Dennis Ritchie", language: 'C'}
]

let cobolProgrammer = famousProgrammers.find((programmer)=>{
  console.log("looking at", programmer.name)
  return programmer.language === "Cobol"
})
console.log(cobolProgrammer)
/*
looking at Rich Hickey
looking at Robert Martin
looking at Grace Hopper
{ name: 'Grace Hopper', language: 'Cobol' }
 */

In the code above, you can see that find() search the list until it found a match, and returned it. This is an efficient way to find a match in a list because the function only runs as many times as it needs to to find a match.

map()

Map is one of the most useful higher order functions available on an array. Map iterates over an array, and makes a new array from the return value of its callback. You can transform an array into another array easily. Consider this example:

let numList = [1, 2, 3]

let newNumList = numList.map((num)=>{
  return num
})

A console.log() of numList and newNumList reveals that we have simply duplicated the numList array using the map() function. It is the return statement that determines what work gets performed on each item in the array before it is saved in the new array. The following example is nearly identical, but notice the calculation performed in the return statement.

let numbers = [1, 2, 3]

let doubled = numbers.map((num)=>{
  return num * 2
})

A console.log() of numbers and doubled reveals that performed work on the items on the numbers array and saved it in a new array called doubled. It is worth noting that this work was performed non-destructively. The numbers array is still intact.

Now let's take a look at an example with map() used on an array of objects:

let famousProgrammers = [
  {name: "Rich Hickey", language: 'Closure'},
  {name:"Robert Martin", language: 'Java'},
  {name: "Grace Hopper", language: 'Cobol'},
  {name: "Richard Stallman", language: 'lisp'},
  {name: "Dennis Ritchie", language: 'C'}
]

let descriptions = famousProgrammers.map((programmer)=>{
  return `${programmer.name} is a ${programmer.language} programmer`
})
console.log(descriptions)
/*
[ 'Rich Hickey is a Closure programmer',
  'Robert Martin is a Java programmer',
  'Grace Hopper is a Cobol programmer',
  'Richard Stallman is a lisp programmer',
  'Dennis Ritchie is a C programmer' ]
 */

You can see that we were returned an array from map() with the text we want. map() is used all the time in React applications, especially in the render function, when we have a list of things we want to display inside a component. There is an example of one such component below. Its map() call may look familiar to you.

import React, { Component } from 'react'
import Items from './store/Items'
import {Link} from 'react-router-dom'

class ItemsIndex extends Component {
  componentWillMount(){
    this.setState({items: Items})
  }
  render(){
      let items = this.state.items.map(function(item){
        return(
          <li key={item.id}>
            <Link to={`/items/${item.id}`} >
              {item.item}
            </Link>
          </li>
        )
      })
    return(
      <ul>
        {items}
      </ul>
    )
  }
}

export default ItemsIndex

As such, you'll want to be very comfortable with this one!

Reduce

Next we're going to look at reduce(). Its kind of like the Swiss Army Knife of higher order array functions. Let's start with a few examples, and then we'll take a closer look at what reduce does, and how to use it.

let numbers = [10, 15, 30]

let numSum = numbers.reduce((sum, number)=>{
  return sum + number
},0)
/*
  55
*/

In this example we see that reduce() allows us to take an array and reduce it to a single thing. We also see that it takes two parameters: a function that determines how the array is reduced, and a starting value.

Now let's try out reduce on an array of objects:

let famousProgrammers = [
  {name: "Rich Hickey", language: 'Closure', years: 15},
  {name:"Robert Martin", language: 'Java', years: 25},
  {name: "Grace Hopper", language: 'Cobol', years: 50},
  {name: "Richard Stallman", language: 'lisp', years: 45},
  {name: "Dennis Ritchie", language: 'C', years: 30}
]

let yearsExperience = famousProgrammers.reduce((sum, programmer)=>{
  return sum + programmer.years
},0)

let paragraph = famousProgrammers.reduce((text, programmer)=>{
  return `${text} ${programmer.name} has been programming ${programmer.language} for ${programmer.years} years.`
},'')

console.log("total experience:", yearsExperience)
console.log(paragraph)
/*
total experience: 165
 Rich Hickey has been programming Closure for 15 years. Robert Martin has been programming Java for 25 years. Grace Hopper has been programming Cobol for 50 years. Richard Stallma
n has been programming lisp for 45 years. Dennis Ritchie has been programming C for 30.
 */

Wow! What happened there?

Recall that reduce reduces an array down into another thing. In the first case, we used it to sum the years experience our programmers have. In the second, we built up a string. We can build up any sort of new object we like using reduce; another array, a string, a number, or even a new object. Reduce gets passed two arguments, a reducer function, and a starting value for the object we're going to reduce into. The collector function gets passed two values as well, the thing we're reducing into, and each value of the array in turn.

For the Summing example, we can write it this way:

let collectorFunction = (sum, item)=>{
  return sum + item.years
}
let startingValue = 0
array.reduce(collectorFunction, startingValue)

Most of the time though, you'll see it written like we did above, where the function is declared inline, as well as the starting value.

Reduce is a swiss army knife

Reduce is so versatile, you could actually learn it, and never learn any of the other higher order functions on array (I don't recommend this though). We can implement all the other operations using just reduce. For example, here's map:

let mapExample = famousProgrammers.reduce((collector, programmer)=>{
  collector.push(`${programmer.name} has been programming ${programmer.language} for ${programmer.years} years.`)
  return collector
},[])
console.log(mapExample)
/*
[ 'Rich Hickey has been programming Closure for 15 years.',
  'Robert Martin has been programming Java for 25 years.',
  'Grace Hopper has been programming Cobol for 50 years.',
  'Richard Stallman has been programming lisp for 45 years.',
  'Dennis Ritchie has been programming C for 30 years.' ]
 */

As a final example. Suppose that we wanted to build a new object that listed the programmers by language. Just pull reduce() out of your superhero toolbelt, and save the day!

let byLanguage = famousProgrammers.reduce((collector, programmer)=>{
  let newProgrammer = Object.assign({}, programmer)
  if(!collector.hasOwnProperty(programmer.language)){
    collector[programmer.language] = [newProgrammer]
  }else{
    collector[programmer.language].push(newProgrammer)
  }
  return collector
},{})
console.log(byLanguage)
/*
{ Closure: [ { name: 'Rich Hickey', language: 'Closure', years: 15 } ],
  Java: [ { name: 'Robert Martin', language: 'Java', years: 25 } ],
  Cobol: [ { name: 'Grace Hopper', language: 'Cobol', years: 50 } ],
  lisp: [ { name: 'Richard Stallman', language: 'lisp', years: 45 } ],
  C: [ { name: 'Dennis Ritchie', language: 'C', years: 30 } ] }
 */

The possibilities are endless!

Higher Order Functions in React

React is built around the functional paradigm which means that we're using higher order functions all over the place. In fact, you've already been using higher order functions in the React programming you've done so far. Let's take a minute to explore in detail some of those patterns that we've used, and now that we know about higher order functions, look at ways we can make better use of them.

Pure and Impure Components

Passing behaviors from our impure components down to our pure components is perhaps the most obvious use of higher order functions in React. Recall that we setup our React applications to have a few smart components that know about the data structure in our app, and give them the ability to communicate with the data store. We want to isolate that type of behavior in as few components as possible so that it is easy to maintain. That helps us to write highly reusable pure components.

Consider the Cat Tinder Project. Here is a simplified version of App.js to show how we are passing functions down to child components:

./App.js

class App extends Component {
  constructor(props){
    super(props)
    this.state = {
      apiUrl: "http://localhost:3000",
      cats: [],
      newCatSuccess: false,
      errors: null
    }
  }

  handleNewcat(params){
    //... communicate with the server
  }

  render() {
    return (
      <Router>
        <div>
          <Route exact path="/" render={props => (
            <Grid>
              <NewCat
                onSubmit={this.handleNewcat.bind(this)}
                errors={this.state.errors && this.state.errors.validations}
              />
              {this.state.newCatSuccess &&
                <Redirect to="/cats" />
              }

            </Grid>
          )} />
        </div>
      </Router>
    );
  }
}

export default App;

Notice the onSubmit={} section of our route. Here you can see that we are passing the behavior we want to happen when the user submits a new cat form into the component. This allows us to write NewCat as a pure component. Its wholy unconcerned with what happens when the user clicks submit. Instead, its focused on the interaction. Higher order functions allow us separate our pure components from our impure ones, and level up in our super powers.

Fetch and promises

Promises themselves are built around higher order functions. Everytime we call Promise.then(<function>), we're using the pattern. Here are the guts from the handleNewCat() callback in the example above. Can you identify the higher order functions here?

handleNewcat(params){
  fetch(`${this.state.apiUrl}/cats`,
    {
      body: JSON.stringify(params),
      headers: {
        'Content-Type': 'application/json'
      },
      method: "POST"
    }
  )
  .then((rawResponse)=>{
    return rawResponse.json()
  })
  .then((parsedResponse) =>{
    if(parsedResponse.errors){
      this.setState({errors: parsedResponse.errors})
    }else{
      const cats = Object.assign([], this.state.cats)
      cats.push(parsedResponse.cat)
      this.setState({
        cats: cats,
        errors: null,
        newCatSuccess: true
      })
    }
  })
}

Did you identify both of those .then() callbacks? Good job. By setting up fetch() as a higher order function, it is unbelievably flexible and reusable. Additionally, fetch is allowed to be a very specialized function. It knows how to communicate with external resources. It does not concern itself with the application logic that will be done once it has fetched its data. It leaves that to the callbacks we pass into the promise it returns. Pretty neat!

Jest tests

All of the Jest tests we've written are higher order functions. Jest handles the running of the tests, and displaying output, leaving the actual testing to the functions that we pass in.

describe("reduce", ()=>{
  it("test something", ()=>{
    //perform our tests
  })
})

React Boostrap Callbacks

The React Bootstrap library we've been using also makes heavy use of higher order functions. This allow us to abstract a huge swath of code, and make it unbeleivably reusable. Imagine if we didn't have higher order functions, and had to write all of that html markup each and every time we need to build a UI component. Our fingers would be exhausted! Instead, we pass callbacks into the React Bootstrap components, and allow them to call back out to the application code we write when appropriate. This pattern is what is known as "separation of concerns". Google it, and you'll learn a lot about how to write clean, reusable code.

Consider <Tooltip> as an example. We can pass many different functions in to get the behavior we want. Here's all of the different ones available:

<Tooltip
  onEnter={}
  onEntered={}
  onEntering={}
  onExit={}
  onExited={}
  onExiting={}
/>

That's a lot of control, all available because Tooltip is a Higher Order Component.

Checkout the React Bootstrap documentation to see all the callbacks we can pass into components, and do some experimenting on your own.

React Router

React Router also makes use of Higher Order Components, so that the <Route/> component can remain pure. The most important one we've looked at already is render(). With render, we can pass in any html structure we like, and the route will dutifully render it when the route matches.

Here's the Route from NewCat again as an example:

render() {
  return (
    <Router>
      <div>
        <Route exact path="/" render={props => (
          <Grid>
           <!-- HTML markup to render -->
          </Grid>
        )} />
      </div>
    </Router>
  );
}

Notice here that the call back is expecting to be passed an argument when it is called. In this case, the props for the component. This highlights the importance of writting good documentation when we create higher order functions that we expect others to use. If the React Router people hadn't documented that callback well, we as programmers would be left scratching our heads over how to pass props into our child components.

Challenges

  • There are many, many more examples of higher order functions throught the React code we've already written. Review the Cat Tinder project, both server and frontend and try to identify some.

  • Revisit the Cat Tinder project and look for ways to refactor your code to make better use of higher order functions

  • Explore the React Bootstrap documentation to find some components that expect behavior to be passed into them, and create a new React application using some of those components. Be creative!

  • Write tests for the behavior functions that you passed into the Bootstrap components above. Be sure to notice how the separation of display logic from behavior has made everything much, much easier to test!