# Advanced React _by React Training_
> Personal notes while working through Advanced React: https://courses.reacttraining.com/p/advanced-react
Granted this is a contrived example, but it's still something I took notice to:
in those "Advanced React" videos I've been watching, Ryan Florence codes very
slowly, and does not make one quick change and jump back to the browser to see
what changed.
He stops and thinks. He asks himself (or the viewer) questions. He wonders what
different approaches might look like. He double checks his work. He is in no rush.
And it is _so much_ more intelligent of a way to program.
The way Ryan Florence handles this course is unlike most tutorials I find. He is
lighthearted and funny throughout the whole series, and that makes watching and
listening to him all the more pleasurable. Code is often taken far too seriously
(I am certainly guilty of this), but Ryan is able to take complex topics and
make them entertaining. His pairing of humor and insight is invaluable, and I
have learned more than just how to code from watching him.
He is also highly experimental in his way of programming, and uses code as a
medium of thought process. When he has a crazy idea, he tries it out; when
something doesn't work, he removes it and tries a different path; when his
spidey-sense is tingling, he double checks his work and is not unduly quick to
mash the save button and see what renders.
I really love the way these exercises are set up. Rather than having you
implement something from a blank canvas, many of the exercises come with some
code that is already "using" the code we are meant to implement. What I mean by
this is that the consumption of the API has already been created for us—what we
need to do is implement the internals of the API.
This provides a great starting point and also helps us in another way. When
writing code on my own now (i.e. starting from a "blank canvas"), I have often
found myself writing my API before I have even decided how I'd like to interact
with it. After watching this series, I have started to write what I would
_expect_ my API to provide before actually writing it.
## Section 01 - Imperative vs. Declarative
The topic of this section is to discuss how to create React components that
isolate imperative operations. We are shown how to create "behavioral
components": components that don't render any UI but just provide some sort of
behavior or state.
### Take Aways
> "One of the things I love about React is that it lets me eliminate time in my
> code; I don't have to think about time, I just have to think about snapshots in
> time. At any time in a React app, we can look at the state of the app and then
> look at the render methods of our components, and we should be able to predict
> what the screen is going render." — Ryan Florence
> "You can tell when you've got a good abstraction not just when you can add
> code easily, but when you can remove code easily." — Michael Jackson
The "behavioral" component `` is a great example of a
component that has this kind of nice abstraction. In order to remove the behavior
it provides, one need only to remove the component and render its children as
they are. _That's it_. The code is very easily removable without breaking any
functionality, and that's wonderful abstraction to work with when building.
This idea of "behavioral" React Components—or components that don't render
anything but just provide some sort of behavior or state—is still quite novel
to me. The examples in this section are intriguing (especially the ``
component from the lecture, which I initially saw a while back at Ryan
Florence's React Rally 2016 talk), and I love how wonderful they package
imperative code, but I still find myself struggling to fit the concepts into my
own development. I suppose this will just take time, and having the knowledge
that they exist will hopefully allow me to recognize when and where they can be
used.
Perhaps a good heuristic to slowly train myself to think about "behavioral"
components would be to isolate my imperative code in a (temporarily named)
`doImperativeStuff` method, and then figure out if I could instead just be
"reacting" to state changes in `componentDidMount` and `componentDidUpdate`.
`componentDidUpdate` can be thought of as React's way of saying _"okay, I've
updated this component's state (and props) and I've updated the DOM in response
to the changed state (and props), is there anything else you would like to react
to? Is there anything else you'd like to do given the state of this app?"_. It
is our chance to "do imperative stuff". It's React giving us a chance to
participate in updating the app in response to a change in state.
#### TL;DR
**Behavioral React Components**: components that don't render anything but just
provide some sort of behavior or state.
`componentDidMount` and `componentDidUpdate` are wonderful React life cycle
hooks that can be used together in order to isolate and perform imperative
operations. They are React's way of giving us a chance to participate in
updating the app in response to a change in state.
### Exercise Solution Code
```javascript
// My solution
class PinScrollToBottom extends Component {
state = {
scrollHeight: undefined
}
componentDidMount () {
const { scrollHeight } = document.documentElement.scrollHeight
window.scrollTo(0, scrollHeight)
this.setState({ scrollHeight })
}
componentDidUpdate (prevProps, prevState) {
const { scrollTop, scrollHeight, clientHeight } = document.documentElement
if (scrollHeight !== prevState.scrollHeight) {
this.setState({ scrollHeight })
}
if (scrollTop === scrollHeight - clientHeight) {
window.scrollTo(0, scrollHeight)
}
}
render () {
return this.props.children
}
}
```
```javascript
// React Training's solution
class PinScrollToBottom extends Component {
componentDidMount () {
this.scroll()
}
componentDidUpdate () {
this.scroll()
}
componentWillUpdate () {
const { clientHeight, scrollTop, scrollHeight } = document.documentElement
this.scrolledUp = clientHeight + scrollTop < scrollHeight
}
scroll () {
if (!this.scrolledUp)
window.scrollTo(0, document.documentElement.scrollHeight)
}
render () {
return this.props.children
}
}
```
### Thoughts on Implementations
I had to spend some time getting familiar with `scrollHeight`, `clientHeight`,
`scrollTop`, and `scrollTo`, as these were methods and properties I had not
previously worked with. Even after reading the MDN pages for these methods and
properties, I had to experiment myself with them to fully grasp what they did.
In my experimentation, I found that `scrollTop === scrollHeight - clientHeight`
was true whenever I was scrolled to the bottom of the page. This obviously felt
like a useful predicate to determine if I should be calling `scrollTo` and
updating the scroll to the bottom of the page (i.e. `scrollHeight`). I also
found that `clientHeight`, by its definition, never changed on a scroll event
(it only changes when I change the height of my browser window).
The biggest mental roadblock I ran into was around _when_ and _how_ I should be
checking and updating the values of the `scrollHeight` and/or `scrollTop`. (Even
just that sentence has so many ands and ors when I write it out now, ha!) It
became clear that I needed to maintain some sort of state.
In hindsight, after seeing the React Training solution, it appears that state
may not have been the best way to handle this, as setting state will run a
component through a render life cycle, and before I had my conditionals straight
this resulted in many a stack overflow and unresponsive browser tabs.
The solution provided by React Training is definitely more elegant than what I
came up with. I love that both `componentDidMount` and `componentDidUpdate`
simply make a call to `scroll`, and there is no component state.
The more I look at it, the more my solution and the solution React Training
provides seem similar. If I moved the `if` statement that makes the call to
`scrollTo` to a `scroll` method, then the only difference between the two
solutions is that mine uses `scrollHeight` state and React Training's uses
`componentWillUpdate` (which can't update state) and an instance method
`scrolledUp`.
I'm endlessly impressed with the ever-growing number of problems React is
capable of handling. Reactively rendering UI is one thing, but creating
components that allow us to just provide behavior or state, and implementing
them in a way that eliminates the need to think about time, is outstanding.
## Section 02 - Compound Components
The topic of this section is on creating compound components and passing around
implicit props in order to make components extendable and changeable in ways
other than an ever-growing list of props.
### Take Aways
> "Our [component] children are really just data. Just because someone told us
> to render these children doesn't mean we have to render them in their current
> form. We can map over them, inspect them, and clone them to render something
> different based on the data the give us." — Ryan Florence
In the beginning of the solution video, Ryan Florence talks about his preferred
building process in React. He likes to start with building out state and then
rendering that state. This way, he can just swap that state around to make sure
that he's got things working right before adding in the event handlers. This
helps him keep things declarative.
---
The third or fourth time watching the lecture video I had an epiphany moment
when I really understood why compound components are useful.
When a component owns all of the rendering, when you have to do something new
or different (e.g. disabling, changing render order, etc.) you end up having to
create and expose a new prop. This is more or less the same as how we used to
create elements with something like jQueryUI, which handled all rendering on an
initial setup, and then exposed some kind of options object to give some
instruction on what to render. But what if in React, instead of having one big
component responsible for rendering, we create a bunch of components that
"compound" together in order to get the unique interaction we're looking for?
---
```javascript
this.handleClick} />
```
React doesn't add and remove event listeners like the above `onClick` prop, but
instead it delegates one click from the top of the document to all of its virtual
DOM elements. So it will just capture the click and check if there is actually a
handler on the virtual element and only call the handler if it's present. But as
the app switches back and forth between disabled and enabled, React isn't adding
and removing event listeners, so code like the above snippet is actually really
cheap in terms of performance.
#### TL;DR
**Implicit state**: non app-level state that the product developer doesn't care
about or see in order to use the component API successfully; in React, this is
generally accomplished by using `React.Children.map` and `React.cloneElement` in
order to implicitly pass state-derived or event-callback props to children
**Component components**: design architecture that helps avoid having one component
in charge of too much rendering or state; the pattern encourages components to
have limit responsibility, which helps identify where certain state and rendering
should be owned in a component composition, rather than exposing all options at
a single, top-level component with an ever-growing list of props
Compound components generally have some level of implicit prop passing in order
to accomplish the task of limited responsibility. Rather than treating children
components as something to be immediately rendered, compound components clone
and extend children components in order pass along useful data.
Ryan Florence's state-first build process:
- state
- render
- swap state around manually for testing
- create event handlers
### Exercise Solution Code
```javascript
// My solution
class RadioGroup extends Component {
state = {
activeIndex: 0
}
selectButtonIndex = activeIndex => {
this.setState({ activeIndex })
}
render () {
return (
)
}
}
class RadioButton extends Component {
render () {
const { isActive, onSelect } = this.props
const className = 'radio-button ' + (isActive ? 'active' : '')
return (
)
}
}
```
```javascript
// React Training's solution
class RadioGroup extends Component {
state = {
value: this.props.defaultValue
}
render() {
const children = React.Children.map(this.props.children, (child) => {
return React.cloneElement(child, {
isActive: child.props.value === this.state.value,
onSelect: () => this.setState({ value: child.props.value })
})
})
return (
);
}
}
class RadioButton extends Component {
render() {
const { isActive, onSelect } = this.props
const className = "radio-button " + (isActive ? "active" : "");
return (
);
}
}
```
### Thoughts on Implementations
This section's lecture video was actually part of the preview for this course,
so I had watched it when the course was initially announced and implemented my
own version of actual radio elements (i.e. ``) for
practice. I like that the exercise here uses radio buttons as it's example, as
actual radio elements are a perfect real-world use-case for this kind of thing.
There is not much of a difference between my solution and the one provided by
React Training, but I did notice that the `defaultValue` prop was not actually
included on `` in the solution source code. I could have just
added it myself, but instead decided to follow the directions to a tee (i.e. not
touching `` at all) and wondered if I could implement a solution without
`defautlValue`.
I ended up just using the child index as the active value, very similar to how
the lecture code was written for tabs. Doing this is more or less the only
difference between my solution and the provided solution.
The only other differences are cosmetic, but perhaps one worth mentioning is
that I inline rendered the `React.Component.map` call and the provided solution
associates this with a local `children` variable. It's nothing more than a
matter of taste, but I do think I prefer the provided solution over my own
inline rendering.
## Section 03 - Context
### Take Aways
With the added overhead React context brings, and with the core team
discouraging its use, I'm unconvinced that using it makes for a better app API.
The lecture on refactoring `` to use context was more code, a new API,
and merely provided the short-sighted benefit of avoiding passing `activeIndex`
and `onSelectTab` as props. The proposition of using context to alleviate a
strict parent-child relationship between compound components seems a bit
contrived, and ultimately adds more abstraction and opaqueness to the code. For
example, in the lecture, why not just add a `className` prop to the ``
and `` components?
Perhaps the lecture refactor was a contrived example because of how small the
benefit was to using context. I am thankful for the context API in popular
libraries like Redux and React Router, as they save me from having to thread
props to deeply nested children. However, I am not sold on it being a pattern
used liberally or without a full understanding of how to isolate its ostensibly
unstable API with higher order components. It should be seen as a second-class
citizen to props and state, and including it in production-level code should not
be taken lightly.
That said, experimentation with context (especially when used with higher order
components) can be a very eye-opening endeavour. It will help expose how
libraries that use context do so intelligently and without the app developer
needing to touch the context themselves.
#### TL;DR
React context is implemented in two parts: a provider and a consumer.
The provider component uses the static `childContextTypes` property in order to
specify the name, type, and requirement of the context it plans to provide, and
then makes use of the `getChildContext` life cycle hook to actually provide a
context object.
The consumer component uses the static `contextTypes` property to specific the
name, type, and requirement of the context it plans to consume. If the context
properties on the provider match the context properties on the consumer, the
consumer can make use of the properties available within the component on
`this.context`.
Context can then be used to share and manage state between components regardless
of any intermediary UI. It acts as a bit of a wormhole between provider and
consumer, breaking the normal boundaries of state management between components
via props.
While context makes it rather easy to pass state around to arbitrary levels of
your component hierarchy, it also obfuscates where a component may be getting
its state and props from. Context is also warned against by the React team due
to its instable and change-prone api.
### Exercise Solution Code
```javascript
class AudioPlayer extends React.Component {
static childContextTypes = {
play: PropTypes.func.isRequired,
pause: PropTypes.func.isRequired,
jumpForward: PropTypes.func.isRequired,
jumpBack: PropTypes.func.isRequired,
jump: PropTypes.func.isRequired,
isPlaying: PropTypes.bool.isRequired,
duration: PropTypes.number.isRequired,
currentTime: PropTypes.number.isRequired
}
state = {
isPlaying: false,
duration: 0,
currentTime: 0
}
getChildContext () {
const { isPlaying, duration, currentTime } = this.state
return {
play: this.play,
pause: this.pause,
jumpForward: this.jumpForward,
jumpBack: this.jumpBack,
jump: this.jump,
isPlaying,
duration,
currentTime
}
}
play = () => {
this.audio.play()
this.setState({ isPlaying: true })
}
pause = () => {
this.audio.pause()
this.setState({ isPlaying: false })
}
jumpForward = seconds => {
this.audio.currentTime += seconds
}
jumpBack = seconds => {
this.audio.currentTime -= seconds
}
jump = seconds => {
this.audio.currentTime = seconds
}
render () {
return (
)
}
}
```
### Thoughts on Implementations
For a while, I was having trouble passing the audio ref through context. Since
the ref prop is not invoked until [after the component is mounted][refs], and
`getChildContext` is only called when state or props are changed, passing the
instance property `this.audio` through context will be undefined in any consumer
component.
One solution I attempted was to set the audio element ref to an ``
state property in `componentDidMount`, something like `this.setState({ audio:
this.audio })`. I figured that since the ref would available after mount, this
would work out fine.
But I then ran into issues where the `` and `` components would
not invoke a re-render when clicked, i.e., I could get the song to play and pause
just fine thanks to the closures created around them, but disabling the buttons
using `audio.paused` (which is what I had initially been using instead of an
`isPlaying` state property) was not working because actions taken on the audio
ref element passed down in context would not cause a re-render.
Another workaround I came up with was to create closures like `play = () => this
.audio.play()` as instance methods in `` and then pass `this.play`
in context. This approach rubbed me the wrong way and is a duplication of what
already exists on the audio element itself, but I ended up choosing this and
moving forward nevertheless.
I don't know why I didn't think of just inlining the functions on the
`getChildContext` return object, but that is certainly the cleanest solution. I
knew there had to be a better way than what I was doing, and continually found
myself down a number of rabbit holes that made the solution appear far more
difficult than it actually was.
---
Updating the `` on click was also challenging. The audio element's
`currentTime` property in seconds, but the best I can get with a user click is a
percentage of the duration. I had to write out a way of converting the progress
percentage from the user click into a (rough) value in seconds, and ended up
using the same approach as the solution video.
---
It's nice to see that as I progressed through this challenge I was hitting the
same checkpoints that Ryan Florence seems to hit (albeit not quite as elegantly;
my learning style was much more debugging and experimentation—the solution video
made it all look so easy, ha!).
#### Video solution bits that I liked
- I liked the spreading of state in `getChildContext`
- I thought it interesting that Ryan Florence uses `null` values for state that
is "unknown" on initial render in a component
- I liked how all of the context was put on an `audio` object rather than as
top-level properties
- Liked the use of a generic `jump` function and the passing of negative values
to jump backwards
#### My solution bits that I liked
- I liked setting `currentTime` back to zero in `onEnded` callback, rather
than leaving it with the progress bar filled in
- I liked using `event.currentTarget` instead of using a ref for the progress
bar click handler
[refs]: https://facebook.github.io/react/docs/refs-and-the-dom.html#adding-a-ref-to-a-dom-element
## Section 04 - Higher Order Components
This section shows off another way to handle the passing around of state and
state management across an application. Unlike other methods we have seen in
previous sections like the use of compound components, `cloneElement`, and
context, the pattern described in section 04 introduces the concept of "higher
order components" that can be used to make state sharing a bit more explicit.
### Take Aways
The term "higher order component" is borrowed from the functional programming
term "higher order function". A "higher order function" is a function that
returns another function, generally used to compose function behavior together
thanks to closure scopes. In a similar vein, a "higher order component" is a
function that takes a component as an argument and return a new component with
some extra behavior baked into it.
A higher order component _is not_ a component that returns a component, because
that is precisely what every React component does by default.
Interesting to think that using `cloneElement` can more or less do the same
thing that higher order components can do, they are just less explicit about it.
It is harder to see that `` is passing state
between `` and ``—there is nothing inherently telling that the
`` is using some state derived from `` because all this looks
like is a component hierarchy.
Something like `withMedia(App)` is a bit more telling; we can infer from the
"decorative" nature of `withMedia` that it is doing something with `App` and
returning something new.
> "If you're used to using classical inheritance to clean up code and share
> behavior across an app, HOCs are a great substitute for that. You're not going
> to see people doing inheritance in React." — Ryan Florence
HOCs are a nice drop-in replacement for inheritance (actually closer to mixins
or multiple-inheritance patterns).
The HOC pattern is something I've worked closely with in the last few months
(using ReactRedux's `conntect` and ReactRouter's `withRouter`), but hadn't taken
the time to study or build out myself.
It also appears to currently be the de facto way of passing state around a React
application, although the "render prop/children as a function" pattern may usurp
HOCs soon, as there are [many pitfalls to HOCs][hoc-pitfalls].
#### TL;DR
Higher order components are functions that take a component as an argument and
return a new component with some extra behavior baked into it.
HOCs A great way to share behavior across an app.
Watch out of common pitfalls of HOCs, including naming collisions when using
multiple HOCs, confusion or indirection on which HOC is providing which props,
and the number or precautions that must be taken when creating an HOC so as to
not strip the original component of any its own implementation details.
### Exercise Solution Code
```javascript
// My solution
const withStorage = (key, default_) => Component => {
return class WithStorage extends React.Component {
state = {
sidebarIsOpen: get(key, default_)
}
componentDidMount () {
this.unsubscribe = subscribe(() => {
this.setState({
sidebarIsOpen: get('sidebarIsOpen')
})
})
}
componentWillUnmount () {
this.unsubscribe()
}
render () {
return
}
}
}
```
```javascript
// React Training's solution
const withStorage = (key, default_) => (Comp) => (
class WithStorage extends Component {
state = {
[key]: get(key, default_)
}
componentDidMount() {
this.unsubscribe = subscribe(() => {
this.setState({
[key]: get(key)
})
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
return (
)
}
}
)
```
### Thoughts on Implementations
> "You'll see this across React generally [where] you'll get some sort of state
> as a prop and then you'll get a function to change that state. This is as
> fundamental as `` components having a `value` prop as well as an
> `onChange` prop." — Ryan Florence
Get some state, get a way to change that state.
Many of the video solution details were lost on me when I attempted a solution
of my own at first.
- I forgot to use a computed property key for my `localStorage` component state
variable (which wouldn't harm the usefulness of the HOC, but would certainly
make the source code difficult to understand for anyone looking at it)
- I didn't think about the naming collisions of calling my HOC component argument
`Component` and people using `Component` as a named export of `React`
- I didn't consider replacing `set` from `local-storage` with a setter like
`setStorage` passed in from our HOC, which removes forcing users to import
and acknowledge the API of `set`
- I didn't think about how to handle props that may be coming in on the
component we are wrapping (need to spread in `this.props` on the returned
component)
- I didn't think about how a consumer who has imported the HOC-wrapped component
will not have access to `static` properties on the wrapped component
(although, to be fair, this one does seem a bit more nuanced, and it would
seem as though many people have deferred to handling it with an external lib)
- I didn't consider adding our wrapped component as a static property of our HOC
(although, to be fair, in my own experience with HOCs I have opted to also
export the base "unwrapped" class as a named export for testing purposes
instead of relying on a static method)
[hoc-pitfalls]: https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce
## Section 05 - Render Prop Callbacks
Section 05 was perhaps the most epiphanic section thus far, and really drives
home what the point of this entire series is: abstracting behavior and state but
still allowing the app to decide what to render based on that state.
The lecture does a better job explaining different types of state and what
patterns are useful for managing both. It introduces the latest pattern for
handling application-level state: render prop callbacks. This pattern addresses
some of the issues we've witnessed using HOCs, and makes it much clearer what it
means to compose state dynamically.
### Take Aways
> "Render callbacks and HOCs solve a lot of the same use-cases—in fact, every HOC
> could be implemented as a render callback. The difference between them is where
> the composition happens." — Ryan Florence
Render callbacks compose dynamically. They occur while we're rendering the
element in the `render` method of a component.
HOCs compose statically. They occur while we are defining our component, not
while we're rendering them.
_App-level state_: state that the developer actually cares about; state the
developer would like to have the ability to interact with (e.g. HOCs)
_Implicit state_: state that isn't relevant to the developer or doesn't need to be
seen or used or cared about by the developer (e.g. `React.cloneElement`, context)
> "I'd like to be able to compose my state as easily as I compose my components
> together." — Ryan Florence
Render props are another great way to move behavior out of one component and
into another so that it can be more sharable and reusable, but the real benefit
is that it allows you to compose your state and share it across the app.
#### TL;DR
> "Everything we've been doing [throughout this series] has been trying to give
> rendering back to the developer, and move [state management and imperative
> operations] somewhere else." — Ryan Florence
We named a prop "render", gave it a function, and in the component's `render`
method that we put the render prop on, we call the function and pass it the
component's state. So now we are not only able to compose the behavior and look
of our components, but we can also compose the state.
Render props are a great way to share _application-level_ state and behavior. If
you wish to share _implicit_ state, use context or `React.cloneElement`.
### Exercise Solution Code
```javascript
// My solution
class GeoAddress extends React.Component {
state = {
address: null
}
componentDidMount () {
this.updateAddress(this.props.coords)
}
componentWillReceiveProps (nextProps) {
if (nextProps.coords !== this.props.coords) {
this.updateAddress(nextProps.coords)
}
}
updateAddress = async (coords) => {
const address = await getAddressFromCoords(coords.lat, coords.lng)
this.setState({ address })
}
render () {
return this.props.render(this.state.address)
}
}
class App extends React.Component {
render () {
return (
)
}
}
```
### Thoughts on Implementations
As Ryan states in the solution video, refactoring components to use a render prop
is incredibly easy—almost literally just cut and paste and add a render method.
This pattern makes it very easy to take some behavior and some state out of a
component and make it reusable.
It's also very apparent that state is being composed together with this pattern.
Since we are using callbacks, we get the power of closures, and this allows
nested components using render prop callbacks to reference parent render prop
callback arguments, thus allowing state to be composed dynamically in the render
method, just like components.
The video for the solution differs slightly than the actual solution code here
in that in the solution video `getAddressFromCoords` errors are actually caught
and the `` error state is set. I also like the use of `||` in the
render prop callback for `` to determine what text to render.
The solution video uses conditional checks inside of `` to only
fetch the address if the coords props is available in order to handle the case
where the fetch may be made before the map is fully loaded. While I understand
that the check is saving us from when we pull `lat` and `lng` off of an undefined
`this.props.coords`, I'm confused as to when this would ever happen. Since we
only render `` if `coords` are available from the ``
render prop callback argument, won't we always have the `coords` as a prop?
Perhaps there is a case where somehow `coords` is not an object? Even if `lat`
and `lng` are `undefined`, the Google Maps API is nice enough to throw a 400
error for invalid coordinates, which we would catch in the error handler
callback. In any case, I suppose it is an easy way to avoid having our app blow
up in case `coords` is not an object.
I like the solution videos use of `componentDidUpdate` life cycle hook to
determine if the address needs to be updated, as opposed to my use of
`componentWillReceiveProps` to perform the same check. The only reason being
that `componentDidUpdate` will be able to use `this.props.coords` (just like
`componentDidMount`) rather than `nextProps.coords`, which is what must be used
with `componentWillReceiveProps`. I also like it because `componentDidMount` and
`componentDidUpdate` seem like a better life cycle pair, and because I always
forget how to spell `componentWillReceiveProps` ;)
> "That's the whole point of this […]. Abstract behavior and state into a
> different component but still allow the app to decide what to render based on
> that state." — Ryan Florence
The above quote really struck a chord with me. Writing software all day, it's
easy to get sucked into syntax and stick to comfortable and known patterns. But
quotes like this one beg the question of what is reality? What is the code for?
Using the render prop callback pattern is immensely beneficial at giving power
back to the developer for how things should render.
> "Good abstractions make it easy to add code and behavior; really good
> abstractions make it really easy to remove code." — Ryan Florence
## Section 06 - Implementing React Router
This was the first section without a lecture, and was an exercise to use the
patterns talked about throughout this course to reimplement React Router. Not
only does it show off how React Router works on a basic level, it provides a
practical application for the patterns discussed throughout this series.
### Take Aways
We used context and we used compound components to pass around some _implicit_
state (i.e. `history`) in order to make an imperative APIs more declarative
(e.g. instead of everyone having to do `history.push` in their anchor tag click
handlers, we can just declare a `` and wrap the behavior in the
component's implementation that the rest of the app doesn't have to know about.)
We also used the render prop callback pattern to provide inline rendering for
`` components. We also saw the use of a `component` prop that allows the
developer to just pass the `` a component they would like to render. This
pattern is very similar to the HOC pattern and the render prop callback pattern,
and in the real React Router package is a way to pass on some [props][route-props] to the
component provided.
### Exercise Solution Code
```javascript
// My solution
class Router extends React.Component {
static childContextTypes = {
history: PropTypes.object.isRequired
}
state = {
location: null
}
getChildContext () {
return {
history: {
push: (to) => this.history.push(to),
location: this.state.location
}
}
}
componentDidMount () {
this.history = createBrowserHistory()
this.setState({ location: this.history.location })
this.unsubscribe = this.history.listen(() => {
this.setState({ location: this.history.location })
})
}
componentWillUnmount () {
this.unsubscribe()
}
render () {
return this.props.children
}
}
class Route extends React.Component {
static contextTypes = {
history: PropTypes.object.isRequired
}
render () {
const { history } = this.context
const { path, exact, render, component: Component } = this.props
if (history.location) {
if (exact && history.location.pathname !== path) {
return null
}
if (!history.location.pathname.match(path)) {
return null
}
}
if (Component) {
return
}
if (render) {
return render()
}
return null
}
}
```
```javascript
// React Training's solution
class Router extends React.Component {
history = createBrowserHistory()
static childContextTypes = {
history: this.history
}
getChildContext() {
return {
history: this.history
}
}
componentDidMount() {
this.unsubscribe = this.history.listen(() => {
this.forceUpdate()
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
return this.props.children
}
}
class Route extends React.Component {
static contextTypes = {
history: PropTypes.object.isRequired
}
render() {
const { path, exact, render, component:Component } = this.props
const { location } = this.context.history
const match = exact
? location.pathname === path
: location.pathname.startsWith(path)
if (match) {
if (render) {
return render()
} else if (Component) {
return
} else {
return null
}
} else {
return null
}
}
}
```
### Thoughts on Implementations
Figuring out how to check if the current location pathname matched the path prop
was quite difficult
The snippet above ("My solution") is a trivial solution I came up with that
includes code to meet the *bare minimum* necessary to make the UI behave as
expected. However, having used React Router since version 4 was released, I knew
there were other things that would be worth implementing to better mimic the
real library. Most notably, I wanted to see if I could pass the correct [props][route-props] to
components rendered within ``. There is an extra commit for my solution
code that shows how I went about this extra step.
I had followed Tyler McGinnis's [Build your own React Router v4][build-react-router-v4] tutorial article
some months ago, but it was obvious I had not retained everything I had learned
from that article when I attempted to reimplement it again here. I inevitably
succumbed to looking at my solution from the tutorial article for pointers on
how to move forward, but still ended up creating a new solution on my own for
this self-propagated exercise extension.
I continually forget that `this.forceUpdate` is a React-provided method, and
therefore continually forget to think about it as a possible solution. Thinking
of this would have come in handy when I was trying to figure out what to do with
changes in the URL. I had settled on creating local component state to update
the `location` context, which was unnecessary since the `history` instance
property was already keeping track of URL changes.
I also forget about creating custom [class instance properties][class-properties] when working with
components, despite never hesitating to create `state` and custom class instance
methods all the time. It's funny to me how complacent we can become with
repetitive syntax and forget what is actually happening under the hood.
The solution video sets `history` as a class instance property on ``, which
is great because it will be set before it would if it were in `componentDidMount`,
which is necessary when using it on context since child consumer components will
likely be expecting it to be available on their initial render. (This would have
saved me from making the check to see if `history.location` was defined or not
in the render method of ``.) This will be helpful to keep in mind when I
develop with imperative APIs passed to children components.
I'm still learning new things that came out in ES6, like the [`startsWith`][startsWith] string
method that Ryan uses in the solution video to match the url location with the
path prop in the `` component. Granted, the actual library needs to keep
a reference of the match to pass down to the rendered component, and `startsWith`
simply returns a boolean, but it's still good to know that this method exists on
strings (as if there weren't already enough substring methods in JavaScript, heh).
Interesting that the React Training solution uses `this.history` for the value
in `childContextTypes`—for one reason or another I had always assumed only
`PropTypes` properties could be used here, but it makes total sense to use any
object as a type. However, the solution video is clever enough to never open the
browser developer tools during the recording, which would have shown an invalid
type warning for the `this.history` being `undefined` on the `` component's
initial render.
[route-props]: https://reacttraining.com/react-router/web/api/Route/Route-props
[startsWith]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
[build-react-router-v4]: https://tylermcginnis.com/build-your-own-react-router-v4/
[class-properties]: https://babeljs.io/docs/plugins/transform-class-properties/
## Section 07 - Implementing React Redux
React Redux is the binding to the Redux state management library for React. In
this exercise-only section we roll our own `` component and `connect`
HOC, the two pillars of React Redux.
### Take Aways
I have actually implemented a good deal of Redux on my own, writing functions
like `createStore` and `combineReducers` by hand in order to understand how they
work. However, I had never attempted to write parts of the React Redux library
on my own before, so accomplishing that in this section's exercise was a lot of
fun. Now all that's left to implement by hand is React itself ;)
It hit me that `mapStateToProps` and `mapDispatchToProps` are not required to
connect React components to Redux, they are niceties that put the app developer
in charge of what to call the state in the component. You could totally just
pass the entire state to the connected component, but having `mapStateToProps`
gives the power back to the developer to decide what slices of state a component
should know about and also what to call them as props.
#### TL;DR
This exercise is more or less the heart of what React Redux is, using context
and HOCs in order to facilitate a pleasant pattern to create apps in React.
### Exercise Solution Code
```javascript
// My solution
const connect = (mapStateToProps, mapDispatchToProps) => {
return (Component) => {
return class extends React.Component {
static contextTypes = {
store: PropTypes.object.isRequired
}
componentDidMount () {
this.unsubscribe = this.context.store.subscribe(() => this.forceUpdate())
}
componentWillUnmount () {
this.unsubscribe()
}
render () {
const { getState, dispatch } = this.context.store
const state = getState()
const propsFromState = mapStateToProps(state)
const propsFromDispatch = mapDispatchToProps(dispatch)
return (
)
}
}
}
}
```
```javascript
// React Training's solution
const connect = (mapStateToProps, mapDispatchToProps) => {
return (Component) => {
return class extends React.Component {
static contextTypes = {
store: PropTypes.object.isRequired
}
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() => {
this.forceUpdate()
})
}
componentWillUnmount() {
this.unsubscribe()
}
render() {
return (
)
}
}
}
}
```
### Thoughts on Implementations
This is the first time that the React Training solution and mine were more or
less exactly the same. The only notable difference between the two
implementations are that spread `this.props` on the component returned by
`connect` in order to preserve and prioritize any props that were added to ``
by the user. However, after playing around with manually passing a `todos` props
to ``, I am seeing now that **React Redux does not prioritize nor preserve
own props on a connected component if they happen to duplicate prop names passed
in via mapStateToProps or mapDispatchToProps.** So really my solution deviates
more from the real library spec, and I should have passed `this.props` before
passing the props from state and dispatch.
Today I learned that passing an uninvoked callback to a function will result in
my app blowing up with errors if the function that invokes the callback is
dependent on a specific receiver (i.e. the right `this`).
```javascript
// `this` will be undefined when the callback is invoked, which will break the app
this.context.store.subscribe(this.forceUpdate)
// thanks to the callback closure, `this` is preserved as the component and the method works as expected
this.context.store.subscribe(() => this.forceUpdate())
```
Many a time I have done something like `stuff.map(toSomethingElse)` (as opposed
to `stuff.map((data) => toSomethingElse(data))`) when I'm piping data from one
function to the next, but I hadn't considered the implications this would have
around this callback using `this`.