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
- reliable tests: write tests with react testing library
- refactor your component with a first approach
- refactor again if you find things you can improve
- implement new features if needed
write tests with enzymerefactor your component with first approachrewrite tests because they are broken- ...
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,288