fsl.tools
// Patterns r10

Data fetch

The four-state shape every data hook actually has: idle / loading / ready / failed. Once you write it down as a machine, the impossible loading-and-error states stop happening.

Code

fetch.fsl
const req = sm`
  idle    'fetch'    loading;
  loading 'resolve'  ready;
  loading 'reject'   failed;
  ready   'fetch'    loading;   // refresh
  failed  'fetch'    loading;   // retry
`;
useReq.tsx
function useReq(url) {
  const [machine] = React.useState(() => req);
  const [, force] = React.useReducer(x => x + 1, 0);

  React.useEffect(() => {
    if (machine.state() !== 'idle') return;
    machine.go('fetch'); force();
    fetch(url)
      .then(r => r.json())
      .then(  () => { machine.go('resolve'); force(); },
              () => { machine.go('reject');  force(); });
  }, [url]);

  return machine.state();
}

Notes

The classic bug: loading=true and error='...' at the same time. Modeled as a machine, it cannot happen — loading and failed are different states.