0
0
React Nativemobile~20 mins

AbortController for cancellation in React Native - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Fetch Data with Cancel
This screen fetches data from a remote API and allows the user to cancel the fetch request anytime.
Target UI
-------------------------
| Fetch Data with Cancel |
-------------------------
| [Fetch Data Button]    |
|                       |
| Data:                  |
| <empty or fetched>     |
|                       |
| [Cancel Fetch Button]  |
-------------------------
Add a button labeled 'Fetch Data' that starts fetching data from https://jsonplaceholder.typicode.com/todos/1
Display the fetched data's title below the button
Add a 'Cancel Fetch' button that cancels the ongoing fetch request
Use AbortController to handle cancellation
Show 'Fetch cancelled' message if fetch is aborted
Disable 'Cancel Fetch' button when no fetch is running
Handle errors gracefully and show error messages
Starter Code
React Native
import React, { useState, useRef } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

export default function FetchCancelScreen() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [cancelled, setCancelled] = useState(false);
  const abortControllerRef = useRef(null);

  const fetchData = () => {
    // TODO: Implement fetch with AbortController
  };

  const cancelFetch = () => {
    // TODO: Implement cancellation
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Fetch Data with Cancel</Text>
      <Button title="Fetch Data" onPress={fetchData} disabled={loading} />
      <View style={styles.dataContainer}>
        {/* TODO: Show data, error, or cancelled message here */}
      </View>
      <Button title="Cancel Fetch" onPress={cancelFetch} disabled={!loading} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20, justifyContent: 'center' },
  title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, textAlign: 'center' },
  dataContainer: { marginVertical: 20, minHeight: 50 },
});
Task 1
Task 2
Task 3
Task 4
Task 5
Task 6
Task 7
Solution
React Native
import React, { useState, useRef } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

export default function FetchCancelScreen() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [cancelled, setCancelled] = useState(false);
  const abortControllerRef = useRef(null);

  const fetchData = () => {
    setLoading(true);
    setError(null);
    setCancelled(false);
    setData(null);

    const controller = new AbortController();
    abortControllerRef.current = controller;

    fetch('https://jsonplaceholder.typicode.com/todos/1', { signal: controller.signal })
      .then(response => {
        if (!response.ok) throw new Error('Network response was not ok');
        return response.json();
      })
      .then(json => {
        setData(json.title);
        setError(null);
        setCancelled(false);
      })
      .catch(err => {
        if (err.name === 'AbortError') {
          setCancelled(true);
          setData(null);
          setError(null);
        } else {
          setError(err.message);
          setData(null);
          setCancelled(false);
        }
      })
      .finally(() => {
        setLoading(false);
        abortControllerRef.current = null;
      });
  };

  const cancelFetch = () => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Fetch Data with Cancel</Text>
      <Button title="Fetch Data" onPress={fetchData} disabled={loading} />
      <View style={styles.dataContainer}>
        {loading && <Text>Loading...</Text>}
        {data && <Text>Data: {data}</Text>}
        {error && <Text style={{ color: 'red' }}>Error: {error}</Text>}
        {cancelled && <Text style={{ color: 'orange' }}>Fetch cancelled</Text>}
      </View>
      <Button title="Cancel Fetch" onPress={cancelFetch} disabled={!loading} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20, justifyContent: 'center' },
  title: { fontSize: 24, fontWeight: 'bold', marginBottom: 20, textAlign: 'center' },
  dataContainer: { marginVertical: 20, minHeight: 50 },
});

We use AbortController to create a controller that can cancel the fetch request. We save it in a useRef so it persists between renders without causing re-renders.

When the user taps 'Fetch Data', we create a new controller and start fetching with its signal. We update states to show loading and clear previous data or errors.

If the fetch succeeds, we show the fetched title. If the fetch is aborted, we show a cancellation message. If any other error happens, we show the error message.

The 'Cancel Fetch' button calls abort() on the controller, which triggers the abort error and cancels the fetch.

We disable buttons appropriately to prevent multiple fetches or cancelling when no fetch is running.

Final Result
Completed Screen
-------------------------
| Fetch Data with Cancel |
-------------------------
| [Fetch Data Button]    |
|                       |
| Data: delectus aut au- |
| tem quia possimus sus- |
| cipit rerum consequun- |
| tur magni dolores eos  |
| qui ratione voluptatem |
| sequi nesciunt.        |
|                       |
| [Cancel Fetch Button]  |
-------------------------
Tap 'Fetch Data' starts loading and disables 'Fetch Data' button
Fetched data title appears below the button after success
Tap 'Cancel Fetch' during loading cancels fetch and shows 'Fetch cancelled'
'Cancel Fetch' button is disabled when no fetch is running
If fetch fails, error message appears in red text
Stretch Goal
Add a pull-to-refresh gesture to re-fetch data
💡 Hint
Use React Native's RefreshControl with ScrollView or FlatList and connect it to fetchData function