Skip to content

larrybotha/eggheadio-simplify-react-apps-with-react-hooks

Repository files navigation

Simplify React Apps With React Hooks

Notes and annotations for Egghead's Simplify React Apps with React Hooks

Folder structure from this gist.

Table of Contents

02. Refactor a Class Component with React hooks to a Function

query.02.js

query.original.js

Firstly, hooks are only available to function components. The first step is to begin moving class properties to function parameters.

Takeaways:

  • hooks can only be used in function components
  • componentDidUpdate containing effects is a good indicator of having to use useEffect with the properties being compared as the values that need to be provided in useEffects array
  • useReducer can be used to replace state in a class component. It accepts a reducer, and the initial state, and returns a state object and the dispatch function for state to be updated. The first argument useReducer accepts is the current state.
  • static contextType = My.Context can be replaced with const context = useContext(My.Context)

03. Handle Deep Object Comparison in React's useEffect hook with the useRef Hook

query.js

query.02.js

Before the previous refactor we were using lodash/isEqual to do a deep comparison of the variables argument passed into the component. If that property happens to be an object, and because it's passed down from the parent component, React will determine that the object is not equal, and fire the effect on every render.

We need to ensure that a deep comparison of the variable is done to prevent unnecessary requests.

To do this:

  • remove the dependencies array from useEffect
  • compare the previous inputs with the new inputs from within useEffect
  • create a ref in which to store the previous inputs without forcing a render when values are updated in the ref
  • create another useEffect which will be responsible for storing the previous inputs once the query useEffect has run

Takeaways:

  • one can do a manual comparison from within useEffect instead of relying on useEffects dependecy array which will only perform shallow equality checks
  • the order of useEffects within a component matter - they are executed synchronously
  • values in a ref are stored on the current property
  • useRef allows one to create a ref, and refs can store anything outside of a component's state or props and be updated without forcing a re-render

04. Safely setState on a Mounted React Component through the useEffect Hook

query.js

query.03.js

Because useEffect is executing an asynchronous request and setState, the dispatch function returned from useReducer, is called when the promise is resolved or rejected, we need to ensure that if the component is unmounted that setState is not called, as useState and useReducers returned updater functions can't be called on unmounted components without throwing errors.

To fix this, we can use:

  • a ref to store the mounted state of the component
  • use useEffect to set the ref to indicate that the component is mounted
  • only call setState if the ref indicates the component is mounted
  • return a callback from within useEffect that will set the ref to indicate the component is no longer mounted
  • provide an empty deps array to useEffect to ensure that it is only called once on mount, and once on unmount

Takeaways:

  • asynchronous operations within useEffect should have guards to ensure that calls to the component's functions are not executed if the component is no longer mounted
  • a ref is a good place to store this state, as we don't want rerenders to be triggered when it is updated
  • useEffects return callback is a sort of teardown function which can be used to undo what useEffect does
  • an empty array as deps for useEffect will result in the effect only being executed on mount, and on unmount

05. Extract Generic React Hook Code into Custom React Hooks

query.js

query.04.js

One can create a custom hook outside of a component that can then be used inside the component. This is useful for hooks that are common, and less so for hooks that are within a single component only.

Takeaways:

  • a custom hook can make use of another custom hook and extend it
  • useRef can be used inside the custom hook in the same way as within the component, allowing one to abstract the logic for whether a component is mounted or not

06. Track Values Over the Course of Renders with React useRef in a Custom usePrevious Hook

query.js

query.05.js

One can extract useRef to a custom hook. In this scenario we've extracted a hook that could be useful for other components that need to evaluate props from previous renders.

Takeaways:

  • the order of hooks is important - the usePrevious hook in this example would be of no value if it were called before any code that needed to evaluate the previous render's props

07. Refactor a React Class Component with useContext and useState Hooks

user/index.js

user/index.orig.js

A simple refactor of a class component to a component function:

  • replace static contextType and this.context with userContext
  • replace component state with useState

08. Refactor a render Prop Component to a Custom React Hook

user/index.js

user/index.07.js

query.js

query.06.js

A render prop is useful, but creates a lot of nesting. By moving the props a render prop provides to a custom hook, one can remove the nesting, and import the custom hook instead.

Furthermore, a render prop can be created from the custom hook by simply creating a component function that accepts props from a parent, and returns the custom hook with those props appled:

const useCustomHook = () => {}

const MyRenderProp = props => useCustomHook(props);

Takeaways:

  • render props can be refactored to custom hooks (given the render prop doesn't have any of its own markup to render)

  • this can be done by:

    1. removing `children` from the render prop's props
    2. returning state directly
    3. exporting a component function that wraps props passed in and returns
        the custom hook so that we still have a render props component
    4. replacing the render prop in the component with the custom hook, getting
        the state provided by the old render prop from the new custom hook
    

09. Handle componentDidMount and componentWillUnmount in React Component Refactor to Hooks

github-client.js

github-client.original.js

Takeaways:

  • useEffect should not be thought of in terms of being equivalent to componentDidMount and componentWillUnmount - a more accurate mental model is that it's a combination of the two and componentDidUpdate
  • useEffect doesn't run synchronously after the first render, as componentDidMount does; it runs asynchronously some time after the first render
  • useState accepts an initialiser function which is run only on first render, and from which the initial state is returned

10. Dynamically Import React Components with React.lazy and Suspense

index.js

index.original.js

Takeaways:

  • React.lazy accepts a function that returns the promise from a dynamic import
  • lazy-loaded components need to be wrapped inside a Suspense component
  • React's Suspense component renders a fallback component while lazy-loaded components are in a pending state
  • An error boundary is required to prevent React from unmounting the app when unhandled exceptions are thrown

11. Preload React Components with the useEffect Hook

home/index.js

home/index.original.js

When users visit the home, we know that they're going to go to the user page next. We're already dynamically loading the routes using React.lazy, but these chunks will only load when a user navigates to the specific routes.

Once the user route has loaded, only then will the request for user data begin.

We can improve on this by preloading the user page from inside the home page, since we know that users will be navigating to the user page from the home page.

To achieve this:

  • use useEffect inside the home page
  • execute the function in useEffect only once, since we only want the chunk loaded once, by providing an empty dependency array
  • use Webpack's import to import the user page inside useEffect

Takeaways:

  • components that are being lazy loaded may do well to be preloaded if we know that a user will be interacting with them from some previous location
  • this would tie in well with UIs that are navigated deterministically
  • useEffect can be used along with Webpack's import from within a component in order to preload components from components we are confident will be preceding the preloaded component

About

Notes and annotations for Egghead's Simplify React Apps with React Hooks https://egghead.io/courses/simplify-react-apps-with-react-hooks

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published