Building a Structured API Call in React Native with Axios and Redux

Building a Structured API Call in React Native with Axios and Redux

When building React Native apps, it’s important to handle state management efficiently, especially when making API calls and dealing with asynchronous data. Using Redux in conjunction with Axios allows you to organize your API interactions in a scalable, maintainable way. In this guide, we’ll take the previous setup of structured API calls and add Redux to manage the global state.


1. Setting Up Redux in React Native

First, install the necessary dependencies:

npm install redux react-redux @reduxjs/toolkit axios        

We’ll use Redux Toolkit for efficient Redux setup.

2. Organizing the Project Structure

Here's an updated structure that includes Redux:

src/
│
├── api/
│   └── AxiosService.ts
│
├── models/
│   └── User.ts
│
├── services/
│   └── UserService.ts
│
├── store/
│   ├── userSlice.ts
│   └── store.ts
│
└── App.tsx
        

3. Setting Up Axios for API Calls

First, let’s set up AxiosService.js as before to manage the HTTP requests.

AxiosService.js:

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';

class AxiosService {
  private instance: AxiosInstance;

  constructor() {
    this.instance = axios.create({
      baseURL: 'https://api.example.com/', // Set your base URL here
      timeout: 5000,
    });

    // Handle the response globally
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => response,
      (error) => Promise.reject(error)
    );
  }

  // GET request
  public get<T>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.instance.get(url, config);
  }

  // POST request
  public post<T>(url: string, data: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>> {
    return this.instance.post(url, data, config);
  }
}

const axiosService = new AxiosService();
export default axiosService;
        

4. Creating the Model Class

Our User.js model class remains the same:

export default class User {
  public id: number;
  public name: string;
  public email: string;

  constructor(id: number, name: string, email: string) {
    this.id = id;
    this.name = name;
    this.email = email;
  }

  // Static method to create a User object from the API response
  public static fromApi(data: any): User {
    return new User(data.id, data.name, data.email);
  }
}
        

5. Creating the User Service

The service layer will call the Axios service and map the response to the User model.

UserService.ts:

import axiosService from '../api/AxiosService';
import User from '../models/User';

class UserService {
  // Fetch a list of users from the API
  public static async getUsers(): Promise<User[]> {
    const response = await axiosService.get<User[]>('/users');
    return response.data.map((user) => User.fromApi(user));
  }

  // Fetch a single user by ID
  public static async getUserById(userId: number): Promise<User> {
    const response = await axiosService.get<User>(`/users/${userId}`);
    return User.fromApi(response.data);
  }

  // Create a new user
  public static async createUser(userData: Partial<User>): Promise<User> {
    const response = await axiosService.post<User>('/users', userData);
    return User.fromApi(response.data);
  }
}

export default UserService;
        

6. Setting Up Redux Store and Slice

We will now configure Redux to manage the global state of our users. Redux Toolkit simplifies the setup.

userSlice.ts:

import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import UserService from '../services/UserService';
import User from '../models/User';

interface UserState {
  users: User[];
  loading: boolean;
  error: string | null;
}

// Initial state
const initialState: UserState = {
  users: [],
  loading: false,
  error: null,
};

// Fetch users asynchronously using Redux Thunk
export const fetchUsers = createAsyncThunk('users/fetchUsers', async () => {
  const users = await UserService.getUsers();
  return users;
});

// User slice
const userSlice = createSlice({
  name: 'users',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchUsers.fulfilled, (state, action: PayloadAction<User[]>) => {
        state.loading = false;
        state.users = action.payload;
      })
      .addCase(fetchUsers.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message || 'Failed to fetch users';
      });
  },
});

export default userSlice.reducer;
        

store.ts:

import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';

// Configure the Redux store
export const store = configureStore({
  reducer: {
    users: userReducer,
  },
});

// Define the RootState type for use with TypeScript
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
        

This sets up the Redux store and defines the types used with TypeScript.


7. Connecting Redux with the React Native Component

In the App.tsx component, we will use the useSelector and useDispatch hooks from React Redux to fetch and display the user data.

App.tsx:

import React, { useEffect } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUsers } from './src/store/userSlice';
import { RootState } from './src/store/store';
import { Provider } from 'react-redux';
import { store } from './src/store/store';

const UserList: React.FC = () => {
  const dispatch = useDispatch();
  const { users, loading, error } = useSelector((state: RootState) => state.users);

  useEffect(() => {
    dispatch(fetchUsers());
  }, [dispatch]);

  return (
    <View style={styles.container}>
      <Text style={styles.header}>User List</Text>
      {loading ? (
        <Text>Loading...</Text>
      ) : error ? (
        <Text>Error: {error}</Text>
      ) : (
        <FlatList
          data={users}
          keyExtractor={(item) => item.id.toString()}
          renderItem={({ item }) => (
            <Text style={styles.user}>{item.name} - {item.email}</Text>
          )}
        />
      )}
    </View>
  );
};

const App: React.FC = () => {
  return (
    <Provider store={store}>
      <UserList />
    </Provider>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    justifyContent: 'center',
  },
  header: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 10,
  },
  user: {
    fontSize: 18,
    marginVertical: 5,
  },
});

export default App;
        

8. Summary

In this example, we:

  • Created a reusable AxiosService for API calls.
  • Defined a User model to structure API data.
  • Used UserService to interact with the API.
  • Integrated Redux with Thunk to manage global state and asynchronous actions.
  • Typed all elements using TypeScript for type safety and better development experience.

By structuring your React Native app with Axios, Redux, and TypeScript, you ensure that your code is scalable, maintainable, and easy to understand.


Hashtags for the Article:

#ReactNative #TypeScript #Axios #ReduxToolkit #APICalls #MobileAppDevelopment #JavaScript #StateManagement #CleanCode #FrontendDevelopment

要查看或添加评论,请登录

Melby T.的更多文章

社区洞察

其他会员也浏览了