Skip to content

Epic React 3: Advanced React Patterns

Context Module Pattern

  • when creating a Context + useReducer
  • if you have common dispatches you want to share
    • just export it from the counter.js alongside the CountProvider and useCount
export { UserProvider, useUser, updateUser };

Why can't we just put the function in the useCount accessor?

  • we'd have to add useCallback to each function because we'd be putting it in the Provider's value
  • won't be able to tree shake (only get the functions we use)
    • every function would have a useCallback because they'd need to be in the dependency array
  • can't lazy load

Prop Collections & Getters

Why

  • less duplication
    • get the shared props
  • example of props to be returned {'aria-pressed': on, onClick: toggle}
function useToggle() {
    const [on, setOn] = React.useState(false);
    const toggle = () => setOn(!on);

    function getTogglerProps({ onClick, ...props } = {}) {
        return {
            "aria-pressed": on,
            onClick: callAll(onClick, toggle),
            ...props,
        };
    }

    return {
        on,
        toggle,
        getTogglerProps,
    };
}

Usage

<Switch {...getTogglerProps({on})} />
<button
  {...getTogglerProps({
    'aria-label': 'custom-button',
    onClick: () => console.info('onButtonClick'),
    id: 'custom-button-id',
  })}
>

State Reducer

  • let the user of the hook pass in their own reducer
    • inversion of control!
  • to avoid duplication, we can do this to just update one case
function toggleStateReducer(state, action) {
    if (action.type === "toggle" && clickedTooMuch) {
        return { on: state.on };
    }
    return toggleReducer(state, action); // default hook reducer
}

Why is it called State reducer?

  • you pass in a reducer that modifies the state

What does the ={} do?

// useToggle() will work
function useToggle({initialOn = false} = {}) {...}

vs

// useToggle() will result in an error
function useToggle({initialOn = false}) {

Control Props

  • Control Props
  • replicating what React does to forms (controlled vs uncontrolled)
    • is there a value that tha sh
  • error when going from uncontrolled to controlled (or vice versa)
    • example: giving React a value and then later assigning it
const on = isControlled ? controlledOn : state.on;

function dispatchWithOnChange(action) {
    if (!isControlled) {
        dispatch(action); // update the redux's state
    }
    const newState = reducer({ ...state, on }, action);
    onChange?.(newState, action);
}

When to warn?

  1. Passing on without onChange
  2. Passing a value for on and later passing undefined or null
  3. Passing undefined or null for on and later passing a value

console.logs always need to be in a useEffect

side effect to check if value is undefined or null

  • readOnly prop
  • setting the initial value of isChange
  • you can put hooks in the if statement because process.env.NODE_ENV never changes
if (process.env.NODE_ENV !== "production") {
    useWarning();
}

Last update: 2022-09-23