React Retrospective

I worked on the following small projects to familiarize myself with React:

While there were many lessons - here are some points that stood out:

Typescript

I found components of more than a few hundred lines of code increasingly difficult to refactor or extend. I ported my ES6 code to Typescript and found this reduced the complexity curve and revealed a few subtle defects in the process.

export type Action =
  | UndoAction
  | RedoAction
  | OpSetTypeAction
  | OpSetColorAction
  | OpCreateAction
  | OpUpdateAction;

export type Dispatch = (action: Action) => void;

Stateless Components

Unsurprisingly, I found stateless components easier to understand and less error prone.

Dependency Injection

I used higher order functions to inject dependencies and isolate tests to easily replicate different corner cases.

const getFeedFn = (userName) => Promise.resolve([]),
  component = create(
    <Feed
      className="Feed"
      userName="jamespaulmoriarty"
      getFeedFn={getFeedFn}
    />
  );

Redux

Redux and its tooling proved effective at detecting and avoiding mutations. I learnt a lot about common javascript functions and their behavior and performance trying to write immutable code.

case ACTION_TYPES.REDO:
  return {
    ...state,
    future: state.future.slice(0, -1),
    history: state.history.slice().concat(state.future.slice(-1)),
  };

It took me some time to appreciate mapDispatchToProps and how I could remove knowledge of despatch from my jsx.

const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    onChangeComplete: (color: any) => {
      dispatch({
        type: ACTION_TYPES.OP_SET_COLOR,
        payload: { color: color.hex },
      });
    },
  };
};

Cloning

Cloning is useful in some more advanced scenarios. e.g. add/override a event listener without knowledge of the props.children component internals.

<ColorPicker>
  <Button variant="contained">
    // ...
  </Button>
</ColorPicker>
function ColorPicker({ children, onChangeComplete, color }) {
  // ...
  return (
    <div>
      {React.cloneElement(children, { onClick: handleClick })}
      // ...
  );

Error Boundaries

I have a lot of appreciation for this pattern and would love to see it applied more widely. I did had some initial difficulty applying them to async component lifecycle.

componentDidMount() {
  // ...
  .catch((error) => this.setState({ error }));
}

render() {
  if (this.state.error) throw this.state.error;
  // ...
}

Create React App

This was the easiest way to be productive quickly without the common catch of being locked-in.