# React
# Miscellanea
JSX
type JSXElementConstructor<P> = | ((props: P) => ReactElement | null) | (new (props: P) => Component<P, any>); type Key = string | number; interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> { type: T; props: P; key: Key | null; } type ReactText = string | number; type ReactChild = ReactElement | ReactText; interface ReactNodeArray extends Array<ReactNode> {} type ReactFragment = {} | ReactNodeArray; interface ReactPortal extends ReactElement { key: Key | null; children: ReactNode; } type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;- JSX prevents XSS — React DOM escapes any values embedded in JSX before rendering them
- string literals in JSX — JSX removes whitespace at the beginning and ending of a line. It also removes blank lines. New lines adjacent to tags are removed; new lines that occur in the middle of string literals are condensed into a single space.
- Booleans, Null, and Undefined Are Ignored
DOM Attributes
- all DOM properties and attributes (including event handlers) should be camelCased
checked,defaultCheckedfor controlled and uncontrolled components, respectivelyclassNamedangerouslySetInnerHTML: {__html: string}—innerHTML, XSS exploitablehtmlFor—foronChange— similar toinputevent, notchangeevent- vender prefix — begin with capital letter except
ms - React will automatically append a “px” suffix to certain numeric inline style properties
suppressContentEditableWarning— Normally, there is a warning when an element with children is also marked ascontentEditable, because it won’t worksuppressHydrationWarning— mismatches whenhydrate- other HTML attributes
- You may also use custom attributes as long as they’re fully lowercase
Functions, or anything as Children
function Repeat(props) { let items = []; for (let i = 0; i < props.numTimes; i++) { items.push(props.children(i)); } return <div>{items}</div>; } function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>This is item {index} in the list</div>} </Repeat> ); }All React components must act like pure functions with respect to their props.
Returning null from a component’s render method does not affect the firing of the component’s lifecycle methods
Specifying the value prop on a controlled component prevents the user from changing the input unless you desire so
side effects
- data fetching, subscriptions, or manually changing the DOM from React components before
- Mutations, subscriptions, timers, logging
HOC
- Wrap the Display Name for Easy Debugging
function withSubscription(WrappedComponent) { class WithSubscription extends React.Component {/* ... */} WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`; return WithSubscription; } function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; } - Don’t Use HOCs Inside the render Method — negative impact on diff
- (non-react) Static Methods Must Be Copied Over — or lost in the returned new component
- refs are not passed through —
refis not really a prop — likekey, it’s handled specially by React
- Wrap the Display Name for Easy Debugging
SCU
- not mutating data to avoid SCU not working correctly
- Immutable.js (opens new window)
Web Components use “class” instead of “className”
Environment Requirements
- React 16 depends on the collection types
MapandSet - a global polyfill in your bundled application, such as core-js or babel-polyfill
- React 16 depends on the collection types
# Accessibility
tbd
# Context
Context
- some data needs to be accessible by many components at different nesting levels
- alternative
- component composition
- pass component as prop
- render props
React.createContextinterface Context<T> { Provider: Provider<T>; Consumer: Consumer<T>; displayName?: string; } function createContext<T>( defaultValue: T, calculateChangedBits?: (prev: T, next: T) => number ): Context<T>;defaultValue— only used when a component does not have a matchingProviderabove it in the tree
Context.Provider— allows consuming components to subscribe to context changestype Provider<T> = ProviderExoticComponent<ProviderProps<T>>; interface ProviderProps<T> { value: T; children?: ReactNode; }- One Provider can be connected to many consumers
- Providers can be nested to override values deeper within the tree
- consumers that are descendants of a Provider will re-render on
valuechange- not subject to the
shouldComponentUpdatemethod - diff by
Object.is()equivalent — do not write an object literal in JSX
- not subject to the
Component.contextTypetype ContextType<C extends Context<any>> = C extends Context<infer T> ? T : never; class Component<P, S> { static contextType?: Context<any>; }- usage — assign with a context lets you consume the nearest current value of that Context type using
this.contextclass Foo extends React.Component<P, S> { static contextType = Ctx context!: React.ContextType<typeof Ctx> }
- usage — assign with a context lets you consume the nearest current value of that Context type using
Context.Consumer— lets you subscribe to a context within a function componenttype Consumer<T> = ExoticComponent<ConsumerProps<T>>; interface ConsumerProps<T> { children: (value: T) => ReactNode; unstable_observedBits?: number; }- function as child
- Consuming Multiple Contexts
function Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext.Consumer> )} </ThemeContext.Consumer> ); }
# Code Splitting
import()React.lazy()function lazy<T extends ComponentType<any>>( factory: () => Promise<{ default: T }> ): LazyExoticComponent<T>; interface LazyExoticComponent<T extends ComponentType<any>> extends ExoticComponent<ComponentPropsWithRef<T>> { readonly _result: T; }- not yet for SSR
- requires that there’s a
<React.Suspense>component higher in the rendering tree
React.Suspenseconst Suspense: ExoticComponent<SuspenseProps>; interface SuspenseProps { children?: ReactNode; /** A fallback react tree to show when a Suspense child (like React.lazy) suspends */ fallback: NonNullable<ReactNode>|null; // I tried looking at the code but I have no idea what it does. // https://github.com/facebook/react/issues/13206#issuecomment-432489986 /** * Not implemented yet, requires unstable_ConcurrentMode */ // maxDuration?: number; }- not yet for SSR
error boundary — handler when other modules fails to load
usage
- router based code splitting
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import React, { Suspense, lazy } from 'react'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </Suspense> </Router> ); - named export — intermediate module reexport
- router based code splitting
# Error Boundaries
Error Boundaries
- catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them
- log those errors, and display a fallback UI
- don’t try to use them for control flow.
- do not catch errors for:
- Event handlers
- Asynchronous code (e.g.
setTimeoutorrequestAnimationFramecallbacks) - Server side rendering
- Errors thrown in the error boundary itself (rather than its children)
- uncaught error — result in unmounting of the whole React component tree
- filenames and line numbers in the component stack trace — Babel plugin (opens new window)
- depends on
Function.name, may need polyfill, or useReact.Component.displayName
- depends on
- catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them
getDerivedStateFromErrorandcomponentDidCatch- A class component becomes an error boundary if it defines either (or both) of the lifecycle methods
static getDerivedStateFromError()orcomponentDidCatch() - In the event of an error, you can render a fallback UI with
componentDidCatch()by calling setState, but this will be deprecated in a future release. Usestatic getDerivedStateFromError()to handle fallback rendering instead.
- A class component becomes an error boundary if it defines either (or both) of the lifecycle methods
example
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, info) { // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } }
# Refs, Forwarding Refs
Ref forwarding
- alternative — explicitly pass a ref as a differently named prop
- an opt-in feature that lets some components take a ref they receive, and pass it further down (in other words, “forward” it) to a child
- useful in two scenarios:
- Forwarding refs to DOM components
- Forwarding refs in higher-order-components
- example
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>; // access by ref.current
React.forwardReffunction forwardRef<T, P = {}>(Component: RefForwardingComponent<T, P>) : ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>; interface RefForwardingComponent<T, P = {}> { (props: PropsWithChildren<P>, ref: Ref<T>): ReactElement | null; propTypes?: WeakValidationMap<P>; contextTypes?: ValidationMap<any>; defaultProps?: Partial<P>; displayName?: string; } interface ForwardRefExoticComponent<P> extends NamedExoticComponent<P> { defaultProps?: Partial<P>; } interface NamedExoticComponent<P = {}> extends ExoticComponent<P> { displayName?: string; }React.createReffunction createRef<T>(): RefObject<T>; interface RefObject<T> { readonly current: T | null; }RefObject.current— DOM element or component instance- may not use the
refattribute on function components
ReactDOM.findDOMNode()function findDOMNode(instance: ReactInstance | null | undefined): Element | null | Text;- an escape hatch used to access the underlying DOM node, deprecated in
StrictMode - In most cases, you can attach a ref to the DOM node and avoid using findDOMNode at all.
- a component may return a fragment with multiple children, in which case
findDOMNodewill return the DOM node corresponding to the first non-empty child - cannot be used on function components.
- an exception will be thrown if called on a component that has not been mounted yet
- an escape hatch used to access the underlying DOM node, deprecated in
callback ref
class CustomTextInput extends React.Component { constructor(props) { super(props); this.textInput = null; this.setTextInputRef = element => { this.textInput = element; }; this.focusTextInput = () => { if (this.textInput) this.textInput.focus(); }; } componentDidMount() { // autofocus the input on mount this.focusTextInput(); } render() { // Use the `ref` callback to store a reference to the text input DOM // element in an instance field (for example, this.textInput). return ( <div> <input type="text" ref={this.setTextInputRef} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } }uncontrolled element
defaultValueprop for<input>like,defaultCheckedfor<input type="checkbox">likevalueandcheckedfor controlled counterparts
- an
<input type="file" />is always an uncontrolled component
# Portals
Portals — a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component
- An event fired from inside a portal will propagate to ancestors in the containing React tree, even if those elements are not ancestors in the DOM tree
ReactDOM.createPortalfunction createPortal(children: ReactNode, container: Element, key?: null | string): ReactPortal;
# Fragment
return an array
render() { return [ "Some text.", <h2 key="heading-1">A heading</h2>, "More text.", <h2 key="heading-2">Another heading</h2>, "Even more text." ]; }- 每个子元素需要使用逗号分隔
- 数组的每一项必须要有 key 值,否则会产生警告
- 字符串必须使用引号
React.Fragmentconst Fragment: ExoticComponent<{ children?: ReactNode }>; // TODO: similar to how Fragment is actually a symbol, the values returned from createContext, // forwardRef and memo are actually objects that are treated specially by the renderer; see: // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/ReactContext.js#L35-L48 // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/forwardRef.js#L42-L45 // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/memo.js#L27-L31 // However, we have no way of telling the JSX parser that it's a JSX element type or its props other than // by pretending to be a normal component. // // We don't just use ComponentType or SFC types because you are not supposed to attach statics to this // object, but rather to the original function. interface ExoticComponent<P = {}> { /** * **NOTE**: Exotic components are not callable. */ (props: P): (ReactElement|null); readonly $$typeof: symbol; }
# Strict Mode
Strict Mode
- does not render any visible UI. It activates additional checks and warnings for its descendants
- do not impact the production build
help with
- Identifying components with unsafe lifecycles
- Warning about legacy string ref API usage
- Warning about deprecated findDOMNode usage
- Detecting unexpected side effects
- doc (opens new window)
- intentionally double-invoking some methods to detect side effects
- Detecting legacy context API
React.StrictModeconst StrictMode: ExoticComponent<{ children?: ReactNode }>;
# API
# Component, memo
function component
type FC<P = {}> = FunctionComponent<P>; type PropsWithChildren<P> = P & { children?: ReactNode }; interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement | null; propTypes?: WeakValidationMap<P>; contextTypes?: ValidationMap<any>; defaultProps?: Partial<P>; displayName?: string; }- not stateless (SFC) — as of recent React versions, function components can no longer be considered 'stateless': React Hooks (opens new window)
React.Componentinterface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> { } class Component<P, S> { static contextType?: Context<any>; constructor(props: Readonly<P>); setState<K extends keyof S>( state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null), callback?: () => void ): void; forceUpdate(callBack?: () => void): void; render(): ReactNode; readonly props: Readonly<P> & Readonly<{ children?: ReactNode }>; state: Readonly<S>; } interface ComponentClass<P = {}, S = any> extends StaticLifecycle<P, S> { new (props: P, context?: any): Component<P, S>; propTypes?: WeakValidationMap<P>; contextType?: Context<any>; contextTypes?: ValidationMap<any>; childContextTypes?: ValidationMap<any>; defaultProps?: Partial<P>; displayName?: string; }ComponentClass— return type of HOCs or type of class expression
React.PureComponentclass PureComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> { }- implements SCU with a shallow prop and state comparison
optional lifecycle
interface ComponentLifecycle<P, S, SS = any> extends NewLifecycle<P, S, SS>, DeprecatedLifecycle<P, S> { componentDidMount?(): void; shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean; componentWillUnmount?(): void; componentDidCatch?(error: Error, errorInfo: ErrorInfo): void; } interface NewLifecycle<P, S, SS> { getSnapshotBeforeUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>): SS | null; componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot?: SS): void; } interface StaticLifecycle<P, S> { getDerivedStateFromProps?: GetDerivedStateFromProps<P, S>; getDerivedStateFromError?: GetDerivedStateFromError<P, S>; } type GetDerivedStateFromProps<P, S> = (nextProps: Readonly<P>, prevState: S) => Partial<S> | null; type GetDerivedStateFromError<P, S> = (error: any) => Partial<S> | null;- the lifecycle diagram (opens new window)
getDerivedStateFromProps— for rare use cases where the state depends on changes in props over time- If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use
componentDidUpdatelifecycle instead. - If you want to re-compute some data only when a prop changes, use a memoization helper instead.
- If you want to “reset” some state when a prop changes, consider either making a component fully controlled or fully uncontrolled with a key instead.
- If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use
getSnapshotBeforeUpdate— enables your component to capture some information from the DOM (e.g. scroll position) before it is potentially changed
React.memo— HOC similar toReact.PureComponentbut for function components// will show `Memo(${Component.displayName || Component.name})` in devtools by default, // but can be given its own specific name interface MemoExoticComponent<T extends ComponentType<any>> extends NamedExoticComponent<ComponentPropsWithRef<T>> { readonly type: T; } type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>; function memo<P extends object>( Component: FC<P>, propsAreEqual?: (prevProps: Readonly<PropsWithChildren<P>>, nextProps: Readonly<PropsWithChildren<P>>) => boolean ): NamedExoticComponent<P>; function memo<T extends ComponentType<any>>( Component: T, propsAreEqual?: (prevProps: Readonly<ComponentProps<T>>, nextProps: Readonly<ComponentProps<T>>) => boolean ): MemoExoticComponent<T>;- for a performance boost in some cases by memoizing the result
- This means that React will skip rendering the component, and reuse the last rendered result.
- By default it will only shallowly compare complex objects in the props object
- for a performance boost in some cases by memoizing the result
# Create Element
React.createElementfunction createElement<P extends DOMAttributes<T>, T extends Element>( type: string, props?: ClassAttributes<T> & P | null, ...children: ReactNode[]): DOMElement<P, T>; function createElement<P extends {}>( type: FunctionComponent<P> | ComponentClass<P> | string, props?: Attributes & P | null, ...children: ReactNode[]): ReactElement<P>; interface ClassAttributes<T> extends Attributes { ref?: LegacyRef<T>; } interface Attributes { key?: Key; } type LegacyRef<T> = string | Ref<T>;React.createFactory— legacy helper
# Transform Element
React.cloneElementfunction cloneElement<P>( element: ReactElement<P>, props?: Partial<P> & Attributes, ...children: ReactNode[]): ReactElement<P>;- the original element’s props with the new props merged in shallowly
- New children will replace existing children
keyandreffrom the original element will be preserved.// almost equivalent but not ref <element.type {...element.props} {...props}>{children}</element.type>
React.isValidChildrenfunction isValidElement<P>(object: {} | null | undefined): object is ReactElement<P>;React.Children— utilities for dealing with thethis.props.childrenopaque data structureconst Children: ReactChildren; interface ReactChildren { map<T, C>(children: C | C[], fn: (child: C, index: number) => T): T[]; forEach<C>(children: C | C[], fn: (child: C, index: number) => void): void; count(children: any): number; only<C>(children: C): C extends any[] ? never : C; toArray<C>(children: C | C[]): C[]; }React.Children.onlythrows an error whenneverReact.Children.toArray— Returns the children opaque data structure as a flat array with keys assigned to each child.- prefixes each key in the returned array so that each element’s key is scoped to the input array containing it
# Hooks
# Concepts
hooks
- use state and other React features without writing a class
- Classes present issues for today’s tools, too. For example, classes don’t minify very well, and they make hot reloading flaky and unreliable.
- With Hooks, you can extract stateful logic from a component
- Hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data)
- plain implementation (opens new window)
- relies on the order in which Hooks are called
useprefix — only created the first time called, return current ones afterwards- eslint-plugin-react-hooks (opens new window)
- no Hook equivalents to the uncommon
getSnapshotBeforeUpdateandcomponentDidCatchlifecycles yet
- use state and other React features without writing a class
rules
- Only Call Hooks at the Top Level — always use Hooks at the top level of your React function
- put condition inside hooks
- only Call Hooks from React function components and custom hooks
- Only Call Hooks at the Top Level — always use Hooks at the top level of your React function
custom hooks
- a JavaScript function whose name starts with ”use” and that may call other Hooks
- do not have a specific signature, can pass information as the return value
- the extract of common patterns
# Basic Hooks
type definations
// Unlike the class component setState, the updates are not allowed to be partial type SetStateAction<S> = S | ((prevState: S) => S); // this technically does accept a second argument, but it's already under a deprecation warning // and it's not even released so probably better to not define it. type Dispatch<A> = (value: A) => void;React.useState()function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>]; function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];- unlike
this.setStatein a class, updating a state variable always replaces it instead of merging it - use multiple times for multiple states
- If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the
Object.iscomparison algorithm.) getDerivedStateFromProps— store previous props in state
- unlike
React.useEffect()— perform side effects in function components// NOTE: callbacks are _only_ allowed to return either void, or a destructor. // The destructor is itself only allowed to return void. type EffectCallback = () => (void | (() => void | undefined)); // The identity check is done with the SameValue algorithm (Object.is), which is stricter than === // TODO (TypeScript 3.0): ReadonlyArray<unknown> type DependencyList = ReadonlyArray<any>; function useEffect(effect: EffectCallback, deps?: DependencyList): void;- you can think of useEffect Hook as
componentDidMount,componentDidUpdate, andcomponentWillUnmountcombined- component needs to do something after render
- every render a new
EffectCallbackis passed, letting the closure of up-to-date states - Unlike
componentDidMountorcomponentDidUpdate, effects scheduled withuseEffectdon’t block the browser from updating the screen- although deferred until after the browser has painted, it’s guaranteed to fire before any new renders
- use
React.useLayoutEffect()for synchronous effects
- Run on Each Update, performs the cleanup when the component unmounts
- if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect
- will apply every effect used by the component, in the order they were specified.
DependencyList— perform effect only when any value of this list change (===) between renders- mount and unmount only effects — pass
[]asdeps Dispatchreturned byuseEffect,useReduceris guaranteed to be stable so it’s safe to omit
- mount and unmount only effects — pass
- ways of use of function in
EffectCallback- move the function inside
EffectCallback, withdepsupdated to the function - move the function outside component,
depsirrelevant of the function - call the function outside
EffectCallback, and make the effect depend on the returned value - add a function to
depsbut wrap its definition into theuseCallbackHook
- move the function inside
- you can think of useEffect Hook as
React.useContext()function useContext<T>(context: Context<T>/*, (not public API) observedBits?: number|boolean */): T;- always re-render when the context value changes
- works as
static contextType = MyContextin a class, or to<MyContext.Consumer>
# Additional Hooks
React.useReducer()— alternative touseState// Unlike redux, the actions _can_ be anything type Reducer<S, A> = (prevState: S, action: A) => S; // types used to try and prevent the compiler from reducing S // to a supertype common with the second argument to useReducer() type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never; type ReducerAction<R extends Reducer<any, any>> = R extends Reducer<any, infer A> ? A : never; function useReducer<R extends Reducer<any, any>, I>( reducer: R, initializerArg: I & ReducerState<R>, initializer: (arg: I & ReducerState<R>) => ReducerState<R> ): [ReducerState<R>, Dispatch<ReducerAction<R>>]; function useReducer<R extends Reducer<any, any>, I>( reducer: R, initializerArg: I, initializer: (arg: I) => ReducerState<R> ): [ReducerState<R>, Dispatch<ReducerAction<R>>]; function useReducer<R extends Reducer<any, any>>( reducer: R, initialState: ReducerState<R>, initializer?: undefined ): [ReducerState<R>, Dispatch<ReducerAction<R>>];- preferable to
useStatewhen you have complex state logic that involves multiple sub-values or when the next state depends on the previous one - avoid passing callbacks down
const TodosDispatch = React.createContext(null); function TodosApp() { // Note: `dispatch` won't change between re-renders const [todos, dispatch] = useReducer(todosReducer); return ( <TodosDispatch.Provider value={dispatch}> <DeepTree todos={todos} /> </TodosDispatch.Provider> ); }
- preferable to
React.useCallback()— memorize callback// useCallback(X) is identical to just using X, useMemo(() => Y) is identical to just using Y. /** * `useCallback` will return a memoized version of the callback that only changes if one of the `inputs` * has changed. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usecallback */ // TODO (TypeScript 3.0): <T extends (...args: never[]) => unknown> function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;- useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders
useCallback(fn, deps)is equivalent touseMemo(() => fn, deps)- often-changing value — use reference value when closure,
useReduceranddispatchas context is more preferablefunction Form() { const [text, updateText] = useState(''); const textRef = useRef(); useEffect(() => { textRef.current = text; // Write it to the ref }); const handleSubmit = useCallback(() => { const currentText = textRef.current; // Read it from the ref alert(currentText); }, [textRef]); // Don't recreate handleSubmit like [text] would do return ( <> <input value={text} onChange={e => updateText(e.target.value)} /> <ExpensiveTree onSubmit={handleSubmit} /> </> ); }function useEventCallback(fn, dependencies) { const ref = useRef(() => { throw new Error('Cannot call an event handler while rendering.'); }); useEffect(() => { ref.current = fn; }, [fn, ...dependencies]); return useCallback(() => { const fn = ref.current; return fn(); }, [ref]); }
React.useMemo()/** * `useMemo` will only recompute the memoized value when one of the `deps` has changed. * * Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in * the second argument. * * ```ts * function expensive () { ... } * * function Component () { * const expensiveResult = useMemo(expensive, [expensive]) * return ... * } * ``` * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usememo */ // allow undefined, but don't make it optional as that is very likely a mistake function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;- as a performance optimization, not as a semantic guarantee
React.useRef()/** * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument * (`initialValue`). The returned object will persist for the full lifetime of the component. * * Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable * value around similar to how you’d use instance fields in classes. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#useref */ // TODO (TypeScript 3.0): <T extends unknown> function useRef<T>(initialValue: T): MutableRefObject<T>; interface MutableRefObject<T> { current: T; }- creates a plain JavaScript object but persists — The only difference between
useRef()and creating a{current: ...}object yourself is thatuseRefwill give you the same ref object on every render. - previous state by
useReffunction usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; }
- creates a plain JavaScript object but persists — The only difference between
React.useImperativeHandle()— used withforwardRef// NOTE: this does not accept strings, but this will have to be fixed by removing strings from type Ref<T> /** * `useImperativeHandle` customizes the instance value that is exposed to parent components when using * `ref`. As always, imperative code using refs should be avoided in most cases. * * `useImperativeHandle` should be used with `React.forwardRef`. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#useimperativehandle */ function useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, deps?: DependencyList): void;- customizes the instance value that is exposed to parent components when using
ref, should be avoided in most cases - example
function FancyInput(props, ref) { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput);
- customizes the instance value that is exposed to parent components when using
React.useLayoutEffect()— identical to useEffect, but it fires synchronously after all DOM mutations, before the browser has a chance to paintReact.useDebugValue()— display a label for custom hooks in React DevTools./** * `useDebugValue` can be used to display a label for custom hooks in React DevTools. * * NOTE: We don’t recommend adding debug values to every custom hook. * It’s most valuable for custom hooks that are part of shared libraries. * * @version 16.8.0 * @see https://reactjs.org/docs/hooks-reference.html#usedebugvalue */ // the name of the custom hook is itself derived from the function name at runtime: // it's just the function name without the "use" prefix. function useDebugValue<T>(value: T, format?: (value: T) => any): void;
# ReactDOM
ReactDOM.render()const render: Renderer; interface Renderer { // Deprecated(render): The return value is deprecated. // In future releases the render function's return type will be void. ( element: SFCElement<any> | Array<SFCElement<any>>, container: Element | null, callback?: () => void ): void; <P, T extends Component<P, ComponentState>>( element: CElement<P, T>, container: Element | null, callback?: () => void ): T; <P>( element: ReactElement<P>, container: Element | null, callback?: () => void ): Component<P, ComponentState> | Element | void; }- Any existing DOM elements inside are replaced when first called. Later calls use React’s DOM diffing algorithm for efficient updates.
ReactDOM.hydrate()const hydrate: Renderer;- hydrate a container whose HTML contents were rendered by
ReactDOMServer - React will attempt to attach event listeners to the existing markup.
- React expects that the rendered content is identical between the server and the client
- intentionally need to render something different on the server and the client —
this.state.isClient, which you can set totrueincomponentDidMount()
- intentionally need to render something different on the server and the client —
- hydrate a container whose HTML contents were rendered by
ReactDOM.unmountComponentAtNode()function unmountComponentAtNode(container: Element): boolean;- Returns
trueif a component was unmounted andfalseif there was no component to unmount.
- Returns
# ReactDOMServer
ReactDOMServer.renderToString()— Render a React element to its initial HTMLfunction renderToString(element: ReactElement): string;- call
ReactDOM.hydrate()on a node that already has this server-rendered markup, React will preserve it and only attach event handlers
- call
ReactDOMServer.renderToStaticMarkup()— Similar torenderToString, except this doesn’t create extra DOM attributes that React uses internally, such asdata-reactroot- useful if you want to use React as a simple static page generator
ReactDOMServer.renderToNodeStream()—renderToStringwhich returns a streamfunction renderToNodeStream(element: ReactElement): NodeJS.ReadableStream;ReactDOMServer.renderToStaticNodeStream()—renderToStaticMarkupwhich returns a streamNoSsrclass NoSsr extends React.Component { mounted = false; state = { mounted: false, }; componentDidMount() { this.mounted = true; if (this.props.defer) { // Wondering why we use two RAFs? Check this video out: // https://www.youtube.com/watch?v=cCOL7MC4Pl0 // // The componentDidMount() method is called after the DOM nodes are inserted. // The UI might not have rendering the changes. We request a frame. requestAnimationFrame(() => { // The browser should be about to render the DOM nodes // that React committed at this point. // We don't want to interrupt. Let's wait the next frame. requestAnimationFrame(() => { // The UI is up-to-date at this point. // We can continue rendering the children. if (this.mounted) { this.setState({ mounted: true }); } }); }); } else { this.setState({ mounted: true }); } } componentWillUnmount() { this.mounted = false; } render() { const { children, fallback } = this.props; return this.state.mounted ? children : fallback; } }
# SyntheticEvent
SyntheticEventinterface BaseSyntheticEvent<E = object, C = any, T = any> { nativeEvent: E; currentTarget: C; target: T; bubbles: boolean; cancelable: boolean; defaultPrevented: boolean; eventPhase: number; isTrusted: boolean; preventDefault(): void; isDefaultPrevented(): boolean; stopPropagation(): void; isPropagationStopped(): boolean; persist(): void; timeStamp: number; type: string; } interface SyntheticEvent<T = Element, E = Event> extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}event pooling
- the
SyntheticEventobject will be reused and all properties will be nullified after the event callback has been invoked - cannot access the event in an asynchronous way
persist()— which will remove the synthetic event from the pool and allow references to the event to be retained by user code- specific event types interface — .d.ts (opens new window)
- the
# Test Utilities
ReactTestUtilsimport ReactTestUtils from 'react-dom/test-utils';Jest
Enzyme
react-testing-library
ShallowRendererimport ShallowRenderer from 'react-test-renderer/shallow';- Shallow rendering lets you render a component “one level deep” and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered
- does not require a DOM