Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Understanding React Hooks

Avatar for Cuong Phan Cuong Phan
December 08, 2021

Understanding React Hooks

Avatar for Cuong Phan

Cuong Phan

December 08, 2021

Other Decks in Programming

Transcript

  1. Hook • A construct that either holds some value for

    a later time or wraps the execution of a set of instructions. It's either storing value or "doing something“ // store value const [state, setState] = useState("hi curious reader 🎯") const ref = useRef("yay 💃") // do something useEffect(() => { sendAMillionDollars.current() }, [])
  2. Key Points • For referential checks, the setState function’s reference

    never changes during re- renders • If the updater function returns the exact same value as the current state, the subsequent re-render is skipped • setState is asynchronous (does not update immediately) • Have to pass the entire object to the useState updater function because the object is replaced, not merged
  3. Initialize State from Function () => { const [token] =

    useState(() => { let token = window.localStorage.getItem("my-token"); return token || "default#-token#" }) return <div>Token is {token}</div> }
  4. Key Points • Although useEffect is deferred until the browser

    has painted, it is still guaranteed to be fired before any re-renders • Timers, heavy fetch requests (like uploading files), sockets — almost always have to be clean up
  5. Do Not Use the State for Infrastructure Data • The

    information on whether this is the first render shouldn't be stored in the state • The infrastructure data, like details about rendering cycle (is first rendering, the number of renderings), timer ids (setTimeout(), setInterval()), direct references to DOM elements, etc. should be stored and updated using references useRef()
  6. Do Not Use the State for Infrastructure Data function MyComponent()

    { const [isFirst, setIsFirst] = useState(true); const [count, setCount] = useState(0); useEffect(() => { if (isFirst) { setIsFirst(false); return; } console.log('The counter increased!'); }, [count]); return ( <button onClick={() => setCount(count => count + 1)}> Increase </button> ); }
  7. Do Not Use the State for Infrastructure Data (solution) function

    MyComponent() { const isFirstRef = useRef(true); const [count, setCount] = useState(0); useEffect(() => { if (isFirstRef.current) { isFirstRef.current = false; return; } console.log('The counter increased!'); }, [count]); return ( <button onClick={() => setCounter(count => count + 1)}> Increase </button> ); }
  8. The Closure • The closure is a function that remembers

    the variables from the place where it is defined, regardless of where it is executed later
  9. Currying function multiply(a) { return function executeMultiply(b) { return a

    * b; } } const double = multiply(2); double(3); // => 6 double(5); // => 10 const triple = multiply(3); triple(4); // => 12
  10. The Stale Closure function createIncrement(incBy) { let value = 0;

    function increment() { value += incBy; console.log(value); } const message = `Current value is ${value}`; function log() { console.log(message); } return [increment, log]; } const [increment, log] = createIncrement(1); increment(); // logs 1 increment(); // logs 2 increment(); // logs 3 // Does not work! log(); // logs "Current value is 0"
  11. Stale Closures of Hooks function WatchCount() { const [count, setCount]

    = useState(0); useEffect(function() { setInterval(function log() { console.log(`Count is: ${count}`); }, 2000); }, []); return ( <div> {count} <button onClick={() => setCount(count + 1) }> Increase </button> </div> ); }
  12. Stale Closures of Hooks (solution) useEffect(function() { const id =

    setInterval(function log() { console.log(`Count is: ${count}`); }, 2000); return function() { clearInterval(id); } }, [count])
  13. Stale Closures of Hooks function DelayedCount() { const [count, setCount]

    = useState(0); function handleClickAsync() { setTimeout(function delay() { setCount(count + 1); }, 1000); } return ( <div> {count} <button onClick={handleClickAsync}>Increase async</button> </div> ); }
  14. Key Points • The value returned from useContext is determined

    by the value prop of the nearest Provider above the calling component in the tree • The rerendering will not be limited to the component consumer; all components related to the context will rerender • If your state is frequently updated, React Context may not be as effective or efficient as a tool like React Redux • If you have static data that undergoes lower-frequency updates such as preferred language, time changes, location changes, and user authentication, passing down props with React Context may be the best option • If the ancestor component uses React.memo or shouldComponentUpdate, a re- render will still happen starting at the component that calls useContext
  15. Key Points • When you have complex state logic that

    utilizes multiple sub-values, or when a state depends on the previous one, you should favor the use of useReducer over useState • Like the setState updater function returned from calling useState, the dispatch method identity remains the same, so it can be passed down to child components instead of callbacks to update the state value held within useReducer
  16. Basic Usage const [state, dispatch] = useReducer(reducer, initialArgument, init); //receives

    current state and dispatched action const reducer = (state, action) => {}; dispatch({type: 'increase', payload: 10});
  17. Key Points • The memoized callback returned by useCallback is

    guaranteed to have the same reference • It’s especially useful when passing callbacks to child components that depend on referential checks to prevent needless re-renders
  18. Basic Usage const memoizedCallback = useCallback(callback, arrayDependency) • useCallback takes

    a callback argument and an array dependency list and returns a memoized callback • useCallback will recompute the memoized callback if any of the array dependency changes
  19. Key Points • Remember the three questions: Is the function

    being memoized an expensive one, is it recomputed after mount, and is the returned value a primitive • It’s especially useful when passing callbacks to child components that depend on referential checks to prevent needless re-renders
  20. Basic Usage const memoizedResult = useMemo(compute, dependencies); • During initial

    rendering, useMemo(compute, dependencies) invokes compute, memoizes the calculation result, and returns it to the component. • If during next renderings the dependencies don't change, then useMemo() doesn't invoke compute but returns the memoized value. • But if dependencies change during re-rendering, then useMemo() invokes compute, memoizes the new value, and returns it.
  21. Key Points • The ref object remains the same all

    through the lifetime of the component, i.e., across re-renders
  22. When • Managing focus, text selection, or media playback •

    Working with imperative animation • Integrating with third-party DOM libraries
  23. Accessing the Previous Props or State function usePrevious(value) { const

    ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; } function Counter() { const [count, setCount] = useState(0); // 👇 look here const prevCount = usePrevious(count) return <h1> Now: {count}, before: {prevCount} </h1>; }
  24. Forwarding Refs • The issue now is that passing a

    ref to this component will return its instance, a React component reference, and not the element we want to focus on
  25. Forwarding Refs const LabelledInput = (props, ref) => { const

    { id, label, value, onChange } = props return ( <div class="labelled--input"> <label for={id}>{label}</label> <input id={id} onChange={onChange} value={value} ref={ref}/> </div> ) } export default React.forwardRef(LabelledInput)
  26. Key Points • The signature for useLayoutEffect is identical to

    useEffect; the difference is the time of execution • Your useLayoutEffect callback/effects will be fired synchronously after all DOM mutations, i.e., before the browser has a chance to paint
  27. Basic Usage useImperativeHandle(ref, createHandle, [arrayDependency]) • useImperativeHandle takes a ref

    object and a createHandle function whose return value “replaces” the stored value in the ref object
  28. Example const App = () => { const inputRef =

    useRef(null); const handleClick = () => { inputRef.current.focus(); }; return ( <> <Input ref={inputRef} /> <button onClick={handleClick}>click to focus child input</button> </> ); } const FancyInput = forwardRef((props, ref) { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; })
  29. Key Points • When a component is wrapped in React.memo(),

    React renders the component and memoizes the result • Before the next render, if the new props are the same, React reuses the memoized result skipping the next rendering • By reusing the memoized content, React skips rendering the component and doesn't perform a virtual DOM difference check
  30. Custom Equality Check of Props • By default React.memo() does

    a shallow comparison of props and objects of props • To customize the props comparison you can use the second argument to indicate an equality check function: React.memo(Component, [areEqual(prevProps, nextProps)]);
  31. When • Use: The best case of wrapping a component

    in React.memo() is when you expect the functional component to render often and usually with the same props • Avoid: If the component isn't heavy and usually renders with different props, most likely you don't need React.memo()
  32. React.memo() and Callback Functions • Every time a parent component

    defines a callback for its child, it creates new function instances • Solution: Let's apply useCallback() to preserve the callback instance between renderings
  33. Profile Before Optimizing • First — profile • Then quantify

    the increased performance (e.g. 150ms vs 50ms render speed increase) • Then ask yourself: does the increased performance, compared to increased complexity
  34. Popular Patterns • Compound Components • Props Collection • Prop

    Getters • State Initializers • State Reducer • Control Props