Intro to React Workshop
Richard Lindsey @Velveeta
Lesson 1
Component Types
Class-based Components
- React's first, and probably still most widely-used form of authoring components
- Makes use of ES6's class syntax, with the extends keyword
- Provides a built-in set of lifecycle methods to help deal with varying render stages
Class-based Components
import React from 'react';
class HelloWorld extends React.Component {
componentDidMount() {
console.log('I mounted!');
}
componentWillUnmount() {
console.log('I am about to unmount!');
}
render() {
return <div>Hello World!</div>
}
}
export default HelloWorld;
Functional Components
- Introduced in React v0.14
- Originally intended to be used for traditionally "dumb" or stateless components
- Have become much more powerful with the new hooks API
- Have no concept of rendering stages outside of what the hooks API provides
Functional Components
import React from 'react';
const HelloWorld = () => <div>Hello World!</div>;
export default HelloWorld;
Lesson 2
Component-based Development
Component-based Development
Traditional Development
- Building applications by pages
- Difficult to reuse code modules
- Very little modularity
- Building applications by services
- Easier to to reuse code modules
- Indirection can become a problem
- Services are cohesive unto themselves, but lose application context
Component-based Development
Development via Components
- Building applications by layers of components
- Components become natural encapsulation boundaries
- Component-based applications tend to read more naturally
- Code reuse is as simple as dropping a component into a view
- UI views can be written as components
- Services can be written as components
- Data stores can be written as components
- ... can be written as components!
Lesson 3
Compound Components
Compound Components
- Declaratively apply behaviors to children
- Declaratively apply transformations to children
- Help to decouple behavioral logic from children
- Can be used to apply additional props to children
Compound Components
- Relies on React.Children and React.cloneElement functions
- React.Children.map to iterate over children
- React.cloneElement to create a copy of the virtual DOM object for a child, with transformed or additional props
- Remember to pass along existing child props
- Risk of prop name collisions with this method
Lesson 4
Controlling Form Components
Controlling Form Components
Controlled Components
- Requires onChange handler
- Similar to Angular's two-way binding
- Allows state to drive UI exclusively
- Realtime validation feedback
Controlling Form Components
Uncontrolled Components
- Uses alternative handlers (onBlur/onSubmit/etc)
- Requires refs to harvest field values
- Requires manually syncing values to other consumers
- Risk of UI getting out of sync with state, if using state
Lesson 5
State vs Props vs Context
State vs Props vs Context
State
- For self-managed component aspects
- Form element values
- isOpen for things like Dropdowns
- setState function for value updates, which triggers a render update cycle
State vs Props vs Context
Props
- For externally-managed component aspects
- data for Grid components
- options for Select components
- Various callbacks for value updates
- Render update cycles are automatically triggered when any external prop changes, as long as the render cascade hasn't been stopped by something like shouldComponentUpdate
State vs Props vs Context
Context
- For externally-managed component aspects in deeper component trees, to avoid prop-drilling
- Can function as a mini data store for a vertical segment of an application
- Various callbacks for value updates
- Provider/Consumer components created via React.createContext, can also be used with useContext hook
- Render update cycles are automatically triggered when any context value changes, as long as the render cascade hasn't been stopped by something like shouldComponentUpdate
State vs Props vs Context
- Feel free to mix and match all 3 for different scenarios
- If using state that's initialized from props, and updated when props change, consider only using props
- Data updates should propagate down the component tree via props, and back up the component tree via callbacks
- State should ideally live at the lowest possible point in the component tree where it's needed amongst any siblings
- Avoid calculating derived values for state/props/context from functions that return new references each time, e.g. array map/filter operations, unless you're memoizing somehow
Lesson 6
State Management With Redux
State Management With Redux
- Helps prevent prop-drilling by subscribing to any global state changes exactly where you need them
- Composable nature is nestable as shallow or as deeply as you need it to be
- Uses fluxy concepts like dispatched actions to control data updates
- Single, global data store, in contrast to flux's multi-store paradigm
- Store structure does not need to mirror application structure
- mapDispatchToProps offers a simplified 2nd form
State Management With Redux
- Avoid storing non-serializable data
- Avoid data duplication
- Either store raw records for use by multiple consumers with different transformations, or
- Store pre-transformed data in dedicated namespaces, and pull that data, ready to use
- Avoid connecting too few or too many components, as too few leads to increased prop-drilling, and too many adds overhead on update cycles
- Avoid the mergeProps option, use a container component with self-bound functions instead
Lesson 7
Memoization
Memoization
Memoizing Components
- React.PureComponent base class for class-based components
- Automatically implements shouldComponentUpdate
- Only uses a shallow compare check against props and state, so context updates could potentially get blocked at this level
- React.memo for functional components
- Uses a similar shallow comparison check against incoming props, so could also potentially block context updates
Memoization
Memoizing Data
- Allows for caching of results from expensive operations
- Allows for consistent object and array references between render cycles
- Tradeoff is increased memory footprint
- Make sure your cache-busting strategy is solid, or else you risk stale component data
- Popular options: reselect, re-reselect, useMemo hook
Lesson 8
Imperative to Declarative
Imperative to Declarative
- Design your components as granular API's
- Props should define UI or behavioral intent
- Lifecycle methods and hook dependency lists allow for setup and teardown, updates, and other timing concerns
- Literally any API can be wrapped in a component for a declarative drop-in, which will be fully aware of application context, and allow natural prop cascades to govern updates
Lesson 9
Testing React Components
Testing React Components
- Test behaviors, the way your component is intended to be used
- E.g. test clicks, value updates, focus/blur, etc
- Use spy functions to assert that relevant handlers are called properly on user interactions
Testing React Components
- Test UI side-effects, the way your component is intended to be consumed programmatically
- Update values and verify that the corresponding UI output is present
- Update values and make sure downstream props are updated accordingly (for compound components, etc)
fn.
Day 1 complete, congratulations!
Richard Lindsey @Velveeta
React Workshop
By Richard Lindsey
React Workshop
- 173