tags : Web Development
These are notes taken while reading the react docs
Basic Theory
Elements
- Elements are the smallest building blocks of React apps.
- Elements are what components are “made of”
- Unlike browser DOM elements, React elements are plain objects, and are cheap to create.
- React DOM takes care of updating the DOM to match the React elements.
- React elements are immutable. Once you create an element, you can’t change its children or attributes.
- Can represent DOM tags, also can represent user-defined components.
React DOM
- React DOM compares the element and its children to the previous one.
- Only applies the DOM updates necessary to bring the DOM to the desired state.
- JSX.Element -> Return value of React.createElement
- React.ReactNode -> Return value of a component
Components
- Components are like JavaScript functions.
- They accept arbitrary inputs (called “props”)
- Return React elements describing what should appear on the screen.
- React treats components starting with lowercase letters as DOM tags.
- So always start component names with a capital letter.
- All React components must act like pure functions with respect to their props.
Controlled components
- An input form element whose value is controlled by React in this way is called a “controlled component”.
State
- State is similar to props, but it is private and fully controlled by the component.
this.props
andthis.state
may be updated asynchronously- You should not rely on their values for calculating the next state.
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
- State is often called local or encapsulated.
- It is not accessible to any component other than the one that owns and sets it
- Neither parent nor child components can know if a certain component is stateful or stateless
- A component may choose to pass its state down as props to its child components, but wouldn’t know whether it came from the parent’s state, props, or was handtyped.
Events
- Here,
e
is a synthetic event.- A cross-browser wrapper around the browser’s native event.
- It has the same interface as the browser’s native event, including
stopPropagation()
andpreventDefault()
- Except the events work identically across all browsers.
- You cannot return false to prevent default behavior in React. You must call
preventDefault
explicitly. - Generally, if you refer to a method without
()
after it, such asonClick={this.handleClick}
, you should bind that method.
// with class fields syntax
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
handleClick = () => { console.log('this is:', this); };
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
// without class fields syntax
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (<button onClick={() => this.handleClick()}> Click me </button>
);
}
}
// passing args
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
Rendering
- You might want a component to hide itself even though it was rendered by another component. To do this return
null
instead of its render output. - If you choose not to assign an explicit key to list items then React will default to using indexes as keys. But prefer if list data has its own keys.
Composition
- Some components don’t know their children ahead of time.
- Use the special
children prop
to pass children elements directly into their output.
- Use the special
const FancyBorder = (props) =>
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
Advanced topics
Accessibility
no notes on a11y now
Code splitting
Basically splitting your bundles into smaller size for faster load times.
import()
// before
import { add } from './math';
console.log(add(16, 26));
// after
import("./math").then(math => {
console.log(math.add(16, 26));
});
React.lazy
React.lazy
currently only supports default exports.
// requires the use of suspense
import React, { Suspense } from 'react';
import Tabs from './Tabs';
import Glimmer from './Glimmer';
const Comments = React.lazy(() => import('./Comments'));
const Photos = React.lazy(() => import('./Photos'));
function MyComponent() {
const [tab, setTab] = React.useState('photos');
function handleTabSelect(tab) {
startTransition(() => {
setTab(tab);
});
return (
<div>
<Tabs onTabSelect={handleTabSelect} />
<Suspense fallback={<Glimmer />}>
{tab === 'photos' ? <Photos /> : <Comments />}
</Suspense>
</div>
);
}
Additionally Error boundaries
can be used to show if any errors while loading.
Context
Context
provides a way to pass data through the component tree without having to passprops
down manually at every level.- Designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
- Apply it sparingly because it makes component reuse more difficult.
React.createContext
- The
defaultValue
argument is only used when a component does not have a matchingProvider
above it in the tree. - passing
undefined
as a Provider value does not cause consuming components to usedefaultValue
.
Context.Provider
- All consumers that are descendants of a
Provider
will re-render whenever the Provider’s value prop changes. - The propagation from
Provider
to its descendant consumers (including.contextType
anduseContext
) is not subject to theshouldComponentUpdate
method
Class.contextType
- Using this property lets you consume the nearest current value of that Context type using
this.context
- You can only subscribe to a single context using this API.
Context.Consumer
- The value argument passed to the function will be equal to the value prop of the closest Provider for this context above in the tree.
- Using this we can subscribe to multiple contexts
<MyContext.Consumer> {value => /* render something based on the context value */} </MyContext.Consumer>
Error Boundaries
A JavaScript error in a part of the UI shouldn’t break the whole app. To solve this problem for React users, React 16 introduces a new concept of an error boundary
.
- Use
static getDerivedStateFromError()
to render a fallback UI after an error has been thrown. - Use
componentDidCatch()
to log error information. - Error boundaries work like a JavaScript
catch {} block
, but for components. - In practice, most of the time you’ll want to declare an error boundary component once and use it throughout your application.
- For example, Facebook Messenger wraps content of the sidebar, the info panel, the conversation log, and the message input into separate error boundaries. If some component in one of these UI areas crashes, the rest of them remain interactive.
- Error boundaries do not catch errors inside event handlers.
Refs
Refs
provide a way to access DOM nodes or React elements created in the render method.
The child to be modified could be:
- An instance of a React component
- It could be a DOM element.
Avoid using refs for anything that can be done declarative. Some usecases:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating with third-party DOM libraries.
class MyComponent extends React.Component {
constructor(props) {
super(props);
// create a ref
this.myRef = React.createRef();
}
focusDiv() {
// we're accessing "current" to get the DOM node
this.myRef.current.focus();
}
render() {
// attached to React elements via the ref
return <div ref={this.myRef} />;
}
}
Value of the ref differs depending on the type of the node:
- When the ref attribute is used on an
HTML element
:- The
ref
receives theunderlying DOM
element as itscurrent
property.
- The
- When the ref attribute is used on a
custom class component
:- The
ref
receives themounted instance of the component
as itscurrent
. - It’s assigned
null
when itunmounts
. - ref updates happen before
componentDidMount
orcomponentDidUpdate
lifecycle methods.
- The
Functional components & Refs
-
You may not use the
ref
attribute “on”function components
. because they don’t have instances:<MyFunctionComponent ref={this.textInput} /> // won't work
- You can use
forwardRef
to work around this. createRef
will always create a new ref: class-based components works(instances)useRef
takes care of returning the same ref each time as on the initial rendering: function components.
- You can use
-
ref
attributeinside
a function component works as long as it’s referring toDOM el
orclass component
Forwarding refs
You might want to have access to a child’s DOM node from a parent component.
- While you could add a ref to the child component, this is not ideal.
- You’d only get the component instance instead of the DOM node
- Won’t work on function components
- Ref forwarding lets components opt into exposing any child component’s ref as their own. (better)
Ref forwarding is an opt-in feature that lets:
- some components take a
ref
they receive - pass it further down (in other words, “forward” it) to a child.
// The second ref argument only exists when you define a component with
// React.forwardRef call.
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Callback refs
You could also use callback refs which are defined inline.
JSX
// When you pass a string literal, its value is HTML-unescaped.
// So these two JSX expressions are equivalent:
<MyComponent message="<3" />
<MyComponent message={'<3'} />
Portals
Portals provide a first-class way to:
- render children into a DOM node
- that exists outside the DOM hierarchy of the parent component.
Usecase:
- When a parent component has an
overflow: hidden
orz-index
style. - For example, dialogs, hovercards, and tooltips.
Techniques
HOC
A higher-order component is a function that takes a component and returns a new component. They are a pattern that emerges from React’s compositional nature.
- These start with a small case letter but return a
container component
- Resist the temptation to modify a component’s prototype (or otherwise mutate it) inside a HOC.
- Instead of mutation, HOCs should use composition, by wrapping the input component in a container component.
- Some uses
- Pass Unrelated Props Through to the Wrapped Component
- Wrap the Display Name for Easy Debugging
render props
Technique for sharing code between React components using a prop
whose value is a function.
- A render prop is a function prop that a component uses to know what to render.
- You can implement most higher-order components (HOC) using a regular component with a render prop.
- You don’t have to use a prop named
render
to use this pattern.
share the state or behavior that one component encapsulates to other components that need that same state.
// children attribute could have been named differently too. it's the
// render-prop
<Mouse children={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>
// same as above
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}
</Mouse>
Skipped sections
- Integrating w other libs
- Optimizing performance
- Profiler
- Strict Mode
- Typechecking w proptypes
- Uncontrolled components
Web Components
- Usually when using react you’ll not be using webcomponents but can mix
- If you are using third-party Web Components, the best solution is to write a React component that behaves as a wrapper for your Web Component.
- Events emitted by a Web Component may not properly propagate through a React render tree. You will need to manually attach event handlers to handle these events within your React components.
Hooks
- Hooks let you use state and other React features without writing a class.
- Hooks allow you to reuse stateful logic without changing your component hierarchy.
- Hooks are functions that let you “hook into” React state and lifecycle features from function components.
- Hooks let you split one component into smaller functions based on:
- what pieces are related (such as setting up a subscription or fetching data)
- rather than forcing a split based on lifecycle methods. (we can make it more predictable with reducer)
useState
- We call it inside a function component to add some local state to it.
- Unlike
this.setState
,useState
doesn’t merge the old and new state together. - State is only created the first time our component renders. During the next renders,
useState
gives us the current state. - Calling
useState
declares a “state variable”.- Normally, variables disappear when the function exits but state variables are preserved by React.
useEffect
- Adds the ability to perform side effects from a function component.
- It’s
componentDidMount
,componentDidUpdate
, andcomponentWillUnmount
unified into a single API. - It doesn’t block the browser from updating the screen.
- React will apply every effect used by the component, in the order they were specified.
- When you call
useEffect
, you’re telling React to run your “effect” function after flushing changes to the DOM.- React guarantees the DOM has been updated by the time it runs the effects.
Two kind of side effects
- Not requiring cleanup
- Requiring cleanup
- If your effect returns a function, React will run it when it is time to clean up.
- effect cleanup phase happens after every re-render, and not just once during unmounting.
- To clean up only once (on mount and unmount), pass (
[]
) as a second argument. This tells React that your effect doesn’t depend on any values from props or state,
useContext
useReducer
useLayoutEffect
measuring the layout
Rules
- Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions.
Custom hooks
When we want reuse some stateful logic between components:
- There were two options:
HOC
andrender props
. - Custom Hooks let you do this, but without adding more components to your tree.
Custom Hooks are more of a convention than a feature.
- If a function’s name starts with
use
and itcalls other Hooks
, we say it is a custom Hook. - When we want to share logic between two JavaScript functions, we extract it to a third function.
- Two components using the same Hook do not share state. We only share logic.