Solving real-world problems by mixing several advanced React patterns and features

High-Order Components
Clone Children
Context
Render Props
Hooks
Compound Components
Control Props
Portals
Code-Splitting
State Reducers
Uncontrolled Components
Forwarding Refs
State Initializers
Error Boundaries
Refs
Memoization
Prop Collections
Prop Getters
Prop Drilling

New React feature/pattern

๐Ÿ‘ good documentation

easy to understand

๐Ÿ‘Ž lack of practical examples

difficult to apply

Mario Beltrรกn

Lead of Frontend Web Engineering at Whalar

@belcoDev

Belco90

mario.dev

RadioGroup

RadioGroup

Toggle

bool: true, false

enumerate: foo, bar qux, ...

<Radio
  value="a"
  name="RadioGroup"
  onChange={handleChange}
  checked={state === 'a'}
>
  option A
</Radio>
<Radio
  value="b"
  name="RadioGroup"
  onChange={handleChange}
  checked={state === 'b'}
>
  option B
</Radio>
<Radio
  value="c"
  name="RadioGroup"
  onChange={handleChange}
  checked={state === 'c'}
>
  option C
</Radio>
<RadioGroup selectedValue={value} name="RadioGroup" onChange={handleChange}>
  <Radio value="a">option A</Radio>
  <Radio value="b">option B</Radio>
  <Radio value="c">option C</Radio>
</RadioGroup>

Before start coding...

How was to program in React v15

  • No context
  • No hooks
  • cWM and cWUpdate were fine
  • No fragments, array or string rendered
  • No error handling
  • No portals

RadioGroup using Clone Children

๐Ÿ‘

  • really easy to implement
  • not special React version needed

๐Ÿ‘Ž

  • not flexible: what if I need nested children?

RadioGroup using Context

๐Ÿ‘

  • simple to implement
  • flexible
  • exposing Choice as shortcut

๐Ÿ‘Ž

  • Context could be hard to unit test

RadioGroup using Context + Hooks

๐Ÿ‘

  • still simple to implement
  • reusable hook that allows access to context within component methods

๐Ÿ‘Ž

  • Context could be hard to unit test
  • Hook could be hard to unit test too

RadioPickerGroup using RadioGroup

๐Ÿ‘

  • Choice and IfChecked shortcuts are quite handy
  • super flexible

๐Ÿ‘Ž

  • Context shape is not clear
  • children check looks dirty (solved using another context)

Enough about Radio Groups

What about that other component you promised? ๐Ÿง

Whalar demo!

What do we actually need to build?

  • dropdown that not closes on selection
  • manage double state: draft and applied
  • maintain draft state until "apply"
  • discard draft state if "cancel"
  • discard draft state if "close"
  • keep values up to date when changed from outside

ActionableDropdown using redux with double slice

ActionableDropdown using Children Refs

๐Ÿ‘

  • avoid duplicated data in redux by keeping draft local
  • HOC Context provider allows to separate context data from compound component
  • force all editable items to follow a standard value and onChange props naming

๐Ÿ‘Ž

  • wrong data workflow
  • not really React-ish way of getting editable children values
  • keeping refs from children into an array is dangerous
  • doesn't restore values while closed

EditableItem

ActionableDropdown

data

ActionableDropdown using Hooks

๐Ÿ‘

  • at least we have migrated one part to hooks

๐Ÿ‘Ž

  • it's broken if we migrate EditableItem to functional component

(useCallback and useMemo)

  • not using them by default
  • if passing data to 3rd party library, could be useful to memoize
  • when returning functions from custom hook, you probably want to memoize

(useCallback and useMemo)

inline anonymous functions have a negligible impact on application performance

โ€“ Matthew Gerstman after deeper investigation with inline functions

ActionableDropdown using Lifting State Up + State Initializers

๐Ÿ‘

  • correct data workflow
  • proper React-ish way of working with editable items children
  • not keeping refs from children in an array
  • now it restores values while closed

๐Ÿ‘Ž

  • logic for updating the component is difficult to follow
  • local state has to be re-init when props are updated

Good opportunities for useReducer:

  • state depends on another state or props
  • logic update is difficult to follow

ActionableDropdown using useReducer

๐Ÿ‘

  • concerns separated
  • component logic simplified, more readable
  • actionable dropdown behavior + draft values extracted into hook for reusability

๐Ÿ‘Ž

  • reducer + actions knowledge
  • not so easy to find where to use the pattern

Summary

  • inject props with Clone Children
  • expose children with Compound Components
  • using Context with consumers implemented in component, HOC and hook
  • how not to use Children Refs
  • hooks + state initializers to keep local draft
  • how and when to useReducer
  • custom hook for generating action creators for our reducer

Testing time

enzyme

JavaScript Testing utility for React that makes it easier to test your React Components' output. You can also manipulate, traverse, and in some ways simulate runtime given the output

enzyme

  • testing implementation details
  • shallow rendering
  • utils for manipulating the component itself
  • snapshot testing
  • hard to maintain
  • can't test context and hooks

enzyme

component

redux

tests

tests

What should we test then?

react testing library

solution for testing React components by providing light utility functions on top of react-dom and react-dom/test-utils, in a way that encourages better testing practices. Its primary guiding principle is: The more your tests resemble the way your software is used, the more confidence they can give you.

react testing library

  • testing behavior with dev props + user interactions
  • rendering component in a simulated DOM (JSDOM) or in the browser
  • utils for querying elements like users find them
  • improves accessibility
  • easy to maintain
  • use cases documentation
  • snapshot testing (just in case)

react testing library

component

redux

tests

tests

tests

react testing library downsides

  • switch your mentality for writing tests
  • learning curve (queries, a11y)
  • difference between unit and integration is not clear
  • weird situations with async queries and act
  • styles with css modules can't be tested
  • complex to test components measuring height or width

Tests examples

Tips

When should I refactor?

do not refactor just because

refactor when 1) revisiting the component or 2) you need to update for using something else (i.e. custom hook)

do not refactor for abstracting a pattern the first time

refactor applying rule of three

How should I refactor

  1. reliable tests: write tests with react testing library
  2. refactor your component with a first approach
  3. refactor again if you find things you can improve
  4. implement new features if needed
  1. write tests with enzyme
  2. refactor your component with first approach
  3. rewrite tests because they are broken
  4. ...

References

I hate coding cause it feels so good to get something to work but then if you try to explain what you did it doesn't sound hard or cool lolol

React Alicante 2019: Solving real-world problems by mixing several advanced React patterns and features

By Mario Beltrรกn

React Alicante 2019: Solving real-world problems by mixing several advanced React patterns and features

Those examples about how to customize the theme of your app or that toggle component sample are fine to see the basic use case of new React patterns and features, but they are not enough to see how powerful these techniques are. In this talk we will see in depth how to put together most of the recent React advanced patterns and features (Compound Components, High-Order Components, State Initializers, Context API, Refs and -of course- Hooks!) to solve complex real-world problems in a simple, yet elegant way, including proper automated tests for them.

  • 1,269