Skip to content

Instantly share code, notes, and snippets.

@lxynox
Last active June 23, 2019 00:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lxynox/bb41ec3c460e807f73a4a3b8220e23a9 to your computer and use it in GitHub Desktop.
Save lxynox/bb41ec3c460e807f73a4a3b8220e23a9 to your computer and use it in GitHub Desktop.
make promise cancellable in react

Problem

  • setState is called on unmounted component
  • race conditions among multiple in-flight requests (responses come back out of order)

Solution

Make the promise cancellable

function makeCancellable = promise => {
  let isCancelled = false
  promise.cancel = () => isCancelled = true
  return promise.then(
    val => isCancelled ? Promise.reject({isCancelled}) : val,
    err => isCancelled ? Promise.reject({isCancelled}) : Promise.reject(err)
  )
}

componentDidMount() {
  this.cancellable = makeCancellable(fetchDataById(id))
    .then(data => this.setState(...))
    .catch(({isCancelled, ...err}) => console.log('request is cancelled:', isCancelled))
}

componentWillUnmount() {
  this.cancellable.cancel()
}

Or with hooks,

React.useEffect(() => {
    let isSubscribed = true
    fetchDataById(id).then(data => {
      if (isSubscribed) {
        setData(data)
      }
    })
    return () => isSubscribed = false
}, [id]);

Step further, even more opinionated, creating a custom hook

// Definition
const usePromise = fn => {
  const [state, setState] = React.useState({
    isLoading: false,
    error: null,
    data: null
  });

  React.useEffect(() => {
    let isSubscribed = true;
    setState({ isLoading: true });
    fn()
      .then(data =>
        isSubscribed ? setState({ data, isLoading: false }) : null
      )
      .catch(error =>
        isSubscribed ? setState({ error, isLoading: false }) : null
      );
    return () => (isSubscribed = false);
  }, [fn]);

  return state;
};

// Usage
const { data, error, isLoading } = usePromise(React.useCallback(() => callApi(id), [id])))

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment