Skip to content

jacobparis/with-react

Repository files navigation

with-react

GitHub Repo stars npm GitHub npm npm GitHub top language

A collection of React components that wrap React hooks to provide a more composable API. Each component accepts a render prop that receives the hook's return value.

This enables a lot of features that previously violate the Rules of Hooks

Conditional Hooks

function UserProfile({ user, showDetails }) {
  return (
    <div>
      <h1>{user.name}</h1>

      <p>
        This is a really long description and there could be more markup
        or other components between here and the top of the component.
        Since hooks need to be declared at the top of the component function,
        instead of in the JSX, you may want to use WithState instead of useState
        just for colocation reasons.
      </p>

      {showDetails && (
        <WithState initialState={user.lastLogin}>
          {(lastLogin, setLastLogin) => (
            <div>
              Last login: {lastLogin}
              <button onClick={() => setLastLogin(new Date())}>
                Update
              </button>
            </div>
          )}
        </WithState>
      )}
    </div>
  )
}

Hooks within loops

This is a special case of conditional hooks, but sometimes you're rendering a lot of items in a loop and it's not worth creating a custom component for them because they require too much data from the parent scope.

But then when you need to add some state to each one, React forces you to wrap it in a component. By using WithState, that component is provided for you and you can keep your list items owned by the parent.

const items = ['apple', 'banana', 'orange']

function FruitList() {
  return (
    <ul>
      {items.map((item) => (
        <WithState key={item} initialState={0}>
          {(count, setCount) => (
            <li>
              {item}: {count}
              <button onClick={() => setCount(count + 1)}>+</button>
            </li>
          )}
        </WithState>
      ))}
    </ul>
  )
}

Form Status

The useFormStatus() hook from react-dom treats any parent <form> like a context provider, and for that reason must be used in a child component of the form.

Avoid creating a component boundary by using WithFormStatus and getting the value inline.

function SearchForm() {
  return (
    <form action="/search">
      <input name="q" />
      <WithFormStatus>
        {(status) => (
          <button disabled={status.pending}>
            {status.pending ? 'Submitting...' : 'Submit'}
          </button>
        )}
      </WithFormStatus>
    </form>
  )
}

Promise Resolution

The use() hook can unwrap promises, but it must be used in a child of the suspense boundary it's meant to trigger. Since data is best fetched at the route level, these promises will almost always be naturally higher than the UI wheir their suspense boundary needs to be.

The WithUse component allows you to pass a promise and get its resolved value directly within it.

This use-case resembles React Router's Await component, which is a better name but I had to stick with the theme here.

function UserDetails() {
  return (
    <div>
      <h1>User Details</h1>
      <Suspense fallback={<Spinner />}>
        <WithUse value={getUserPromise}>
          {(user) => (
            <div>
              <p>Name: {user.name}</p>
              <p>Email: {user.email}</p>
            </div>
          )}
        </WithUse>
      </Suspense>
    </div>
  )
}

Available Components

WithState and WithFormStatus are the most useful

Hook Component
useActionState WithActionState
useCallback WithCallback
useContext X
useDeferredValue WithDeferredValue
useEffect X
useFormStatus WithFormStatus
useId WithId
useImperativeHandle X
useInsertionEffect X
useLayoutEffect X
useMemo WithMemo
useReducer WithReducer
useRef X
useState WithState
useSyncExternalStore WithSyncExternalStore
useTransition WithTransition
use WithUse

About

Components for every React hook

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors