On Reacting Well

ES6

  • Array
    • forEach
    • map
    • filter
    • find
    • keys
    • reduce
  • Object
    • Destructuring
    • Assign
  • Spread Operator

Other

  • HOC
  • Single Responsability
  • Composition over Inher.
  • Functional Programming
  • Pure Component
  • Curring
  • Pipe
  • 3 principles
  • Middlewares
    • ReduxThunk
    • ReduxPromise
  • Flat Structure
  • Immutability

Redux

  • create-react-app
  • reselect (for selectors in Redux)
  • recompose (withState, branch, lifecycle, compose, withHandlers)
  • Storybook (Addons -> Actions, Knobs)
  • Kea.js

Tools

ES6 most useful iteration methods and stuff

Array

  • forEach
  • map
  • filter
  • find
  • findIndex
  • reduce

Object

  • assign
  • keys

destructuring

spread operator

Array methods

  • forEach
  • map
  • filter
  • find
  • findIndex
  • reduce

Array.prototype.forEach()

The forEach() method executes a provided function once for each array element.

const arr = ['a', 'b', 'c'];

arr.forEach(element => console.log(element));

// a
// b
// c
arr.forEach(function callback(currentValue, index, array) {
    //your iterator
}[, thisArg]);

Syntax

Array.prototype.map()

The map() method creates a new array with the results of calling a provided function on every element in the calling array.

const numbers = [2, 4, 8, 10];
const halves = numbers.map(x => x / 2);

const numbers = [1, 4, 9];
const roots = numbers.map(Math.sqrt);
// roots is now [1, 2, 3]
// numbers is still [1, 4, 9]
var new_array = arr.map(function callback(currentValue, index, array) {
    // Return element for new_array
}[, thisArg])

Syntax

Array.prototype.filter()

The filter() method creates a new array with all elements that pass the test implemented by the provided function.

const words = ["spray", "limit", "elite", "exuberant"];

const longWords = words.filter(word => word.length > 6);
var new_array = arr.filter(function callback(currentValue, index, array) {
    // Return element for new_array if it passes the test
}[, thisArg])

Syntax

Array.prototype.find()

The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.

const isBigEnough = element => element >= 15;

[12, 5, 8, 130, 44].find(isBigEnough); 
// 130
var result = arr.find(function callback(currentValue, index, array) {
    // Return first element that passes the test
}[, thisArg])

Syntax

Array.prototype.findIndex()

The findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned.

const isBigEnough = element => element >= 15;

[12, 5, 8, 130, 44].findIndex(isBigEnough);

// index of 4th element in the Array is returned,
// so this will result in '3'
const index = arr.filter(function callback(currentValue, index, array) {
    
}[, thisArg])

Syntax

Array.prototype.reduce()

The reduce() method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.

const total = [ 0, 1, 2, 3 ].reduce(( acc, cur ) => acc + cur, 0);
// 6

const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

const countedNames = names.reduce((allNames, name) => { 
  if (name in allNames) allNames[name]++;
  else allNames[name] = 1;
  return allNames;
}, {});

// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
const result = arr.filter(function callback(accumulator, currentValue, currentIndex, array) {
    // Return element for new_array
}[, initialValue])

Syntax

Object methods

  • assign
  • keys

Object.assign()

The Object.assign()  method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

// object cloning
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }

// object merging
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };

const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
const newObj = Object.assign(target, ...sources)

Syntax

Object.keys()

The Object.keys() method returns an array of a given object's own enumerable properties.

const arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); 
// console: ['0', '1', '2']

// array like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); 
// console: ['0', '1', '2']
const propsNames = Object.keys(obj)

Syntax

Destructuring and spread operator

Destructuring assignment

The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.

// array destructuring
const foo = ['one', 'two', 'three'];
const [one, two, three] = foo;
console.log(one, two, three); // one two three

// object destructuring
const o = {p: 42, q: true};
const {p, q} = o;
console.log(p, q); // 42 true

// aliases
const o = {p: 42, q: true};
const {p: foo, q: bar} = o;
console.log(foo, bar); // 42 true

// default values
const {a = 10, b = 5} = {a: 3};
console.log(a, b); // 3 5

// aliases and default values
const {a:aa = 10, b:bb = 5} = {a: 3};
console.log(aa, bb); // 3 5

// destructuring function parameters
const arr = [{ id: 111 }, { id: 222 }];
const arrayIds = arr.map(({ id }) => id);
console.log(arrayIds); // [111, 222]

Spread Operator

It allows an expression to be expanded in places where multiple elements/variables/arguments are expected.

const middle = [3, 4];
const arr = [1, 2, ...middle, 5, 6];
console.log(arr); // [1, 2, 3, 4, 5, 6]

// array copy
const arr = ['a', 'b', 'c'];
const arr2 = [...arr];
console.log(arr2); // ['a', 'b', 'c']

// array concat
const arr = ['a', 'b', 'c'];
const arr2 = ['d', 'e', 'f'];
arr = [...arr, ...arr2];
console.log(arr); // ['a', 'b', 'c', 'd', 'e', 'f']

// React component props
const obj = { name: 'Ale', age: '16' };
<ComponentBadge ...obj />
// is equal to
<ComponentBadge name={obj.name} age={obj.age} />

High Order Components

A Higher Order Component is just a React Component that wraps another one.

What can I do with HOCs?

At a high level HOC enables you to:

  • Code reuse, logic and bootstrap abstraction
  • Render Highjacking
  • State abstraction and manipulation
  • Props manipulation

HOC factory implementations

There are two main ways of implementing HOCs in React:

  • Props Proxy (PP)
  • Inheritance Inversion (II)

 

Both enable different ways of manipulating the WrappedComponent.

Props Proxy

export const ppHOC = WrappedComponent =>
  class PP extends React.Component {
    render() {
      return <WrappedComponent {...this.props} />;
    }
  };

// <WrappedComponent {...this.props}/>
// is equivalent to
// React.createElement(WrappedComponent, this.props, null)

What can be done with Props Proxy?

  • Manipulating props
  • Accessing the instance via Refs
  • Abstracting State
  • Wrapping the WrappedComponent with other elements

PP - Manipulating props

export const ppHOC = WrappedComponent =>
  class PP extends React.Component {
    render() {
      const newProps = {
        user: currentLoggedInUser
      };
      return <WrappedComponent {...this.props} {...newProps} />;
    }
  };

You can read, add, edit and remove the props that are being passed to the WrappedComponent.

Adding new props. The app’s current logged in user will be available in the WrappedComponent via this.props.user

PP - Accessing the instance via Refs

export const refsHOC = WrappedComponent =>
  class RefsHOC extends React.Component {
    proc = wrappedComponentInstance => {
      wrappedComponentInstance.method();
    };

    render() {
      const props = Object.assign({}, this.props, { ref: this.proc });
      return <WrappedComponent {...props} />;
    }
  };

You can access this (the instance of the WrappedComponent) with a ref, but you will need a full initial normal render process of the WrappedComponentfor the ref to be calculated

In the example we explore how to access instance methods and the instance itself of the WrappedComponent via refs

PP - State abstraction

export const ppHOC = WrappedComponent =>
  class PP extends React.Component {
    constructor(props) {
      super(props);
      this.state = { name: '' };
      this.onNameChange = this.onNameChange.bind(this);
    }

    onNameChange(event) {
      this.setState({ name: event.target.value });
    }

    render() {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onNameChange
        }
      };
      return <WrappedComponent {...this.props} {...newProps} />;
    }
  };

You can abstract state by providing props and callbacks to the WrappedComponent, very similar to how smart components will deal with dumb components.

We abstract the value and onChange handler of the name input field

@ppHOC
class Example extends React.Component {
  render() {
    return <input name="name" {...this.props.name}/>
  }
}

...use it like this

The input will be a controlled input automagically.

PP - Wrapping the WrappedComponent with other elements

export const ppHOC = WrappedComponent =>
  class PP extends React.Component {
    render() {
      return (
        <div style={{ display: 'block' }}>
          <WrappedComponent {...this.props} />
        </div>
      );
    }
  };

You can wrap the WrappedComponent with other components and elements for styling, layout or other purposes.

Wrapping for styling purposes

Inheritance Inversion

export const iiHOC = WrappedComponent =>
  class Enhancer extends WrappedComponent {
    render() {
      return super.render();
    }
  };

Instead of the WrappedComponent extending some Enhancer class, it is passively extended by the Enhancer. In this way the relationship between them seems inverse.

Inheritance Inversion allows the HOC to have access to the WrappedComponent instance via this, which means it has access to the state, props, component lifecycle hooks and the render method.

What can you do with Inheritance Inversion?

  • Render Highjacking
  • Manipulating state

In Render Highjacking you can:

  • Read, add, edit, remove props in any of the React Elements outputted by render
  • Read, and modify the React Elements tree outputted by render
  • Conditionally display the elements tree
  • Wrapping the element’s tree for styling purposes (as shown in Props Proxy)

 

*render refers to the WrappedComponent.render method

export const iiHOC = WrappedComponent =>
  class Enhancer extends WrappedComponent {
    render() {
      this.props.loggedIn ? super.render() : null;
    }
  };

This HOC will render exactly what the WrappedComponent would render unless this.props.loggedIn is not true. (Assuming the HOC will receive the loggedIn prop)

II - Conditional rendering

export const iiHOC = WrappedComponent =>
  class Enhancer extends WrappedComponent {
    render() {
      const elementsTree = super.render();
      let newProps = {};
      if (elementsTree && elementsTree.type === 'input') {
        newProps = { value: 'may the force be with you' };
      }
      const props = Object.assign({}, elementsTree.props, newProps);
      const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children);
      return newElementsTree;
    }
  };

If the rendered output of the WrappedComponent has an input as it’s top level element then we change the value to “may the force be with you”.

You can do all sorts of stuff in here, you can traverse the entire elements tree and change props of any element in the tree.

II - Modify rendering

export const IIHOCDEBUGGER = WrappedComponent =>
  class II extends WrappedComponent {
    render() {
      return (
        <div>
          <h2>HOC Debugger Component</h2>
          <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
          <p>State</p>
          <pre>{JSON.stringify(this.state, null, 2)}</pre>
          {super.render()}
        </div>
      );
    }
  };

The HOC can read, edit and delete state of the WrappedComponent instance, and you can also add more state if you need to.

Mostly the HOC should be limited to read or add state, and the latter should be namespaced not to mess with the WrappedComponent’s state.

II - Manipulating state

export const MakeHOCFactory = (...params) =>
  // do something with params
  WrappedComponent =>
    class HOC extends React.Component {
      render() {
        return <WrappedComponent {...this.props} />;
      }
    };

The HOC can read, edit and delete state of the WrappedComponent instance, and you can also add more state if you need to.

HOC with parameters

Use it like this

MakeHOCFactory(params)(WrappedComponent)
// or with a decorator
@MakeHOCFatory(params)
class WrappedComponent extends React.Component{}

Single Responsibility Principle

A component has a single responsibility when it has one reason to change

Having one responsibility restricts the component size and makes it focused on one thing. A component focused on one thing is convenient to code, and later modify, reuse and test.

Why we give multiple responsability

  • You start coding right away: no need to recognize the responsibilities and plan the structure accordingly
  • One big component does it all: no need to create components for each responsibility
  • No split - no overhead: no need to create props and callbacks for communication between split components.

Such naive structuring is easy to code at the beginning. Difficulties will appear on later modifications, as the application grows and becomes more complex.

The main problem: changing the component for one reason unintentionally influences how other responsibilities are implemented by the same component.

Such design is fragile. Unintentional side effects of are hard to predict and control.

import axios from 'axios';
// Problem: A component with multiple responsibilities
class Weather extends Component {
  constructor(props) {
    super(props);
    this.state = { temperature: 'N/A', windSpeed: 'N/A' };
  }

  render() {
    const { temperature, windSpeed } = this.state;
    return (
      <div className="weather">
        <div>Temperature: {temperature}°C</div>
        <div>Wind: {windSpeed}km/h</div>
      </div>
    );
  }

  componentDidMount() {
    axios.get('http://weather.com/api').then(response => {
      const { current } = response.data;
      this.setState({
        temperature: current.temperature,
        windSpeed: current.windSpeed
      });
    });
  }
}

The weather component has 2 reasons to change

  1. Fetch logic in componentDidMount(): server URL or response format can be modified
  2. Weather visualization in render(): the way component displays the weather can change several times

The solution is to divide <Weather> in 2 components: each having one responsibility. Let's name the chunks <WeatherFetch> and <WeatherInfo>.

import axios from 'axios';
// Solution: Make the component responsible only for fetching
class WeatherFetch extends Component {
  constructor(props) {
    super(props);
    this.state = { temperature: 'N/A', windSpeed: 'N/A' };
  }

  render() {
    const { temperature, windSpeed } = this.state;
    return <WeatherInfo temperature={temperature} windSpeed={windSpeed} />;
  }

  componentDidMount() {
    axios.get('http://weather.com/api').then(response => {
      const { current } = response.data;
      this.setState({
        temperature: current.temperature,
        windSpeed: current.windSpeed
      });
    });
  }
}
// Solution: Make the component responsible for displaying the weather
export const WeatherInfo = ({ temperature, windSpeed }) => (
  <div className="weather">
    <div>Temperature: {temperature}°C</div>
    <div>Wind: {windSpeed} km/h</div>
  </div>
);

Applying composition with chunking components by responsibilities doesn't always help to conform to single responsibility principle.

HOC TO THE RESCUE!

class PersistentForm extends Component {  
  constructor(props) {
    super(props);
    this.state = { inputValue: localStorage.getItem('inputValue') };
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    const { inputValue } = this.state;
    return (
      <div className="persistent-form">
        <input type="text" value={inputValue} 
          onChange={this.handleChange}/> 
        <button onClick={this.handleClick}>Save to storage</button>
      </div>
    );
  }

  handleChange(event) {
    this.setState({
      inputValue: event.target.value
    });
  }

  handleClick() {
    localStorage.setItem('inputValue', this.state.inputValue);
  }
}

On input field change component's state gets updated inside handleChange(event)method. On button click the value is saved to local storage in handleClick():

Unfortunately <PersistentForm> has 2 responsibilities:

  1. manage form fields
  2. saving the input value to store
class PersistentForm extends Component {
  constructor(props) {
    super(props);
    this.state = { inputValue: props.initialValue };
    this.handleChange = this.handleChange.bind(this);
    this.handleClick = this.handleClick.bind(this);
  }

  render() {
    const { inputValue } = this.state;
    return (
      <div className="persistent-form">
        <input type="text" value={inputValue} onChange={this.handleChange} />
        <button onClick={this.handleClick}>Save to storage</button>
      </div>
    );
  }

  handleChange(event) {
    this.setState({
      inputValue: event.target.value
    });
  }

  handleClick() {
    this.props.saveValue(this.state.inputValue);
  }
}
export const withPersistence = (storageKey, storage) => WrappedComponent =>
  class PersistentComponent extends Component {
    constructor(props) {
      super(props);
      this.state = { initialValue: storage.getItem(storageKey) };
    }

    render() {
      return (
        <WrappedComponent
          initialValue={this.state.initialValue}
          saveValue={this.saveValue}
          {...this.props}
        />
      );
    }

    saveValue(value) { storage.setItem(storageKey, value); }
  };

One responsability: render form fields and attach event handlers.

Now its only reason to change is the form field modifications.

It has one focused job: provide the wrapped component with initialValue string and saveValue() function

withPersistence() is a HOC which responsibility is persistence. It doesn't know any details about form fields.

const LocalStoragePersistentForm = withPersistence('key', localStorage)(PersistentForm);

const instance = <LocalStoragePersistentForm />;

// or 

export default withPersistence('key', localStorage)(PersistentForm);

Use HOC like this:

export default withPersistence('key', localStorage)(MyOtherForm);

or with another form

export default withPersistence('key', sessionStorage)(PersistentForm);

or with another type of storage

Composition

over

Inheritance

Inheritance

You decide your types around what they are.

Composition

You decide your types around what they do.

It has limitations.

Murder-Robot-Dog

game example

you got a dog that barks

you got a cat that meows too

they both need to poop

 

but this way is duplication...

so we put poop in a shared animal class

now we got a lot of animals pooping everywhere

so we need a CleaningRobot that can drive and clean

we also need a MurderRobot that can drive and kill all the cats and dogs pooping around

so it all looks like this:

You feel good about it

“Our customers demand a MurderRobotDog. It needs to be able to .kill(), .drive(), .bark(), but it cannot poop()."

And now, we’re screwed

We simply cannot fit the MurderRobotDog nicely into this inheritance hierarchy.

We could create a new parent object, where you put all the functionality that is shared:

But that means that your objects will have a ton of functionality that they don’t use, so you end up with a Gorilla/Banana problem

The other bad route that you can take is to duplicate functionality:

That’s not as bad, but it’s still yucky duplication!

dog            = pooper + barker
cat            = pooper + meower
cleaningRobot  = driver + cleaner
murderRobot    = driver + killer
murderRobotDog = driver + killer + barker

COMPOSITION TO THE RESCUE!

const barker = state => ({ 
    bark: () => console.log(`BAU! I am ${state.name}`)
})

const driver = state => ({
    driver: () => state.position = state.position + state.speed
})

barker({ name: 'Pito' }).bark()
// BAU! I am Pito

In practice...

These functions are like factories, but instead of creating their own state internally, they accept their state as function parameter.

const murderRobotDog = name => {
    let state = {
        name,
        speed: 100,
        position: 0
    }

    return Object.assign(
        {},
        barker(state),
        driver(state),
        killer(state)
    )
}

const roboPito = murderRobotDog('RoboPito')
roboPito.bark() 
// "BAU! I am RoboPito"

First, it creates a state object. It assigns some default values to it — speed and position, and also assigns the name from the funciton argument.

 

Object.assign it takes an object, in this case a new, empty object, and assign the properties from other objects to it.

Is that you’re encouraged to predict the future.

Inheritance encourages you to build this taxonomy of objects very early on in your project, and you are most likely going to make design mistakes doing that, because humans cannot predict the future (even though it feels like we can), and getting out of these inheritance taxonomies is a lot harder than make them work.

So, what's the problem with Inheritance??

So, when to use Inheritance??

Never

It’s just better to use composition in the first place.

It’s more flexible, powerful, and it’s also very easy to do, so why not.

On Reacting Well

By Alessandro Annini

On Reacting Well

  • 633