Integrating React Native with GraphQL: A Comprehensive Guide

Heys devs!

React Native is a powerful tool for developing cross-platform mobile applications, while GraphQL offers a flexible and efficient approach to consuming APIs. Together, they can make app development faster and less error-prone. In this post, …


This content originally appeared on DEV Community and was authored by Paulo Messias

Heys devs!

React Native is a powerful tool for developing cross-platform mobile applications, while GraphQL offers a flexible and efficient approach to consuming APIs. Together, they can make app development faster and less error-prone. In this post, we will explore how to set up and use GraphQL in a React Native application with TypeScript, including installation, code examples (queries and mutations), tests, and best practices.

Installation

1. Setting Up the Environment

First, ensure you have your React Native development environment set up. If you haven't done this yet, follow the instructions in the official documentation to configure the React Native CLI.

2. Creating a New React Native Project

Create a new React Native project using the following command:

npx react-native init MyGraphQLApp --template react-native-template-typescript
cd MyGraphQLApp

3. Installing Necessary Dependencies

To use GraphQL with React Native, we'll need some additional libraries. The main one is Apollo Client, a popular GraphQL client library for JavaScript.

Install Apollo Client and other necessary dependencies:

npm install @apollo/client graphql

Setting Up Apollo Client

1. Configuring Apollo Client

Create a file named ApolloClient.ts in your project's src folder:

// src/ApolloClient.ts

import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

const httpLink = createHttpLink({
  uri: 'https://your-graphql-endpoint.com/graphql',
});

const authLink = setContext((_, { headers }) => {
  const token = 'your-auth-token'; // Replace with your authentication token
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    }
  };
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});

export default client;

2. Setting Up ApolloProvider

In the App.tsx file, configure ApolloProvider to provide the Apollo client to the entire application:

// App.tsx

import React from 'react';
import { ApolloProvider } from '@apollo/client';
import client from './src/ApolloClient';
import HomeScreen from './src/HomeScreen';

const App: React.FC = () => {
  return (
    <ApolloProvider client={client}>
      <HomeScreen />
    </ApolloProvider>
  );
};

export default App;

Consuming Data with GraphQL

1. Writing a GraphQL Query

Create a queries.ts file in the src folder to store your queries:

// src/queries.ts

import { gql } from '@apollo/client';

export const GET_DATA = gql`
  query GetData {
    data {
      id
      name
      description
    }
  }
`;

2. Using the Query in a Component

In your HomeScreen.tsx component, use the GraphQL query with Apollo's useQuery hook:

// src/HomeScreen.tsx

import React from 'react';
import { View, Text, ActivityIndicator, FlatList } from 'react-native';
import { useQuery } from '@apollo/client';
import { GET_DATA } from './queries';

interface DataItem {
  id: string;
  name: string;
  description: string;
}

interface GetDataResult {
  data: DataItem[];
}

const HomeScreen: React.FC = () => {
  const { loading, error, data } = useQuery<GetDataResult>(GET_DATA);

  if (loading) return <ActivityIndicator testID="loading" size="large" color="#0000ff" />;
  if (error) return <Text>Error: {error.message}</Text>;

  return (
    <View>
      <FlatList
        data={data?.data}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View>
            <Text>{item.name}</Text>
            <Text>{item.description}</Text>
          </View>
        )}
      />
    </View>
  );
};

export default HomeScreen;

Executing Mutations with GraphQL

1. Writing a GraphQL Mutation

Create a mutations.ts file in the src folder to store your mutations:

// src/mutations.ts

import { gql } from '@apollo/client';

export const ADD_DATA = gql`
  mutation AddData($name: String!, $description: String!) {
    addData(name: $name, description: $description) {
      id
      name
      description
    }
  }
`;

2. Using the Mutation in a Component

In your HomeScreen.tsx component, use the GraphQL mutation with Apollo's useMutation hook:

// src/HomeScreen.tsx

import React, { useState } from 'react';
import { View, Text, TextInput, Button, ActivityIndicator, FlatList } from 'react-native';
import { useQuery, useMutation } from '@apollo/client';
import { GET_DATA } from './queries';
import { ADD_DATA } from './mutations';

interface DataItem {
  id: string;
  name: string;
  description: string;
}

interface GetDataResult {
  data: DataItem[];
}

interface AddDataVars {
  name: string;
  description: string;
}

const HomeScreen: React.FC = () => {
  const { loading, error, data } = useQuery<GetDataResult>(GET_DATA);
  const [addData] = useMutation<DataItem, AddDataVars>(ADD_DATA);

  const [name, setName] = useState('');
  const [description, setDescription] = useState('');

  const handleAddData = () => {
    addData({ 
      variables: { name, description },
      refetchQueries: [{ query: GET_DATA }]
    });
  };

  if (loading) return <ActivityIndicator testID="loading" size="large" color="#0000ff" />;
  if (error) return <Text>Error: {error.message}</Text>;

  return (
    <View>
      <TextInput 
        placeholder="Name"
        value={name}
        onChangeText={setName}
        testID="name-input"
      />
      <TextInput 
        placeholder="Description"
        value={description}
        onChangeText={setDescription}
        testID="description-input"
      />
      <Button title="Add Data" onPress={handleAddData} testID="add-button" />

      <FlatList
        data={data?.data}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View>
            <Text>{item.name}</Text>
            <Text>{item.description}</Text>
          </View>
        )}
      />
    </View>
  );
};

export default HomeScreen;

Setting Up Tests

1. Installing Test Dependencies

Let's install the necessary libraries for testing. We will use jest, react-native-testing-library, and @testing-library/react-native.

npm install --save-dev jest @testing-library/react-native @testing-library/jest-native @types/jest

2. Configuring Jest

Add the Jest configuration to your package.json:

"jest": {
  "preset": "react-native",
  "setupFilesAfterEnv": [
    "@testing-library/jest-native/extend-expect"
  ],
  "transformIgnorePatterns": [
    "node_modules/(?!(jest-)?react-native|@react-native|@react-native-community|@testing-library|@react-navigation)"
  ]
}

Writing Tests

1. Testing Apollo Client

We'll create a mock of the Apollo Client to use in our tests. Create a file called ApolloMockProvider.tsx in the src/test folder:

// src/test/ApolloMockProvider.tsx

import React, { ReactNode } from 'react';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
import { MockedProvider } from '@apollo/client/testing';

interface Props {
  children: ReactNode;
  mocks: any[];
}

const ApolloMockProvider: React.FC<Props> = ({ children, mocks }) => {
  const client = new ApolloClient({
    cache: new InMemoryCache(),
  });

  return (
    <MockedProvider mocks={mocks} addTypename={false}>
      <ApolloProvider client={client}>
        {children}
      </ApolloProvider>
    </MockedProvider>
  );
};

export default ApolloMockProvider;

2. Testing the HomeScreen Component

Create a test file called HomeScreen.test.tsx in the src/__tests__ folder:

// src/__tests__/HomeScreen.test.tsx

import React from 'react';
import { render, waitFor, fireEvent } from '@testing-library/react-native';
import HomeScreen from '../HomeScreen';
import ApolloMockProvider from '../test/ApolloMockProvider';
import { GET_DATA, ADD_DATA } from '../queries';
import { MockedResponse } from '@apollo/client/testing';

const mocks: MockedResponse[] = [
  {
    request: {
      query: GET_DATA,
    },
    result: {
      data: {
        data: [
          { id: '1', name: 'Test Name', description:
 'Test Description' },
        ],
      },
    },
  },
  {
    request: {
      query: ADD_DATA,
      variables: {
        name: 'New Name',
        description: 'New Description',
      },
    },
    result: {
      data: {
        addData: {
          id: '2',
          name: 'New Name',
          description: 'New Description',
        },
      },
    },
  },
];

describe('HomeScreen', () => {
  it('renders loading state initially', () => {
    const { getByTestId } = render(
      <ApolloMockProvider mocks={[]}>
        <HomeScreen />
      </ApolloMockProvider>
    );
    expect(getByTestId('loading')).toBeTruthy();
  });

  it('renders data correctly', async () => {
    const { getByText } = render(
      <ApolloMockProvider mocks={mocks}>
        <HomeScreen />
      </ApolloMockProvider>
    );

    await waitFor(() => {
      expect(getByText('Test Name')).toBeTruthy();
      expect(getByText('Test Description')).toBeTruthy();
    });
  });

  it('adds new data correctly', async () => {
    const { getByPlaceholderText, getByText } = render(
      <ApolloMockProvider mocks={mocks}>
        <HomeScreen />
      </ApolloMockProvider>
    );

    const nameInput = getByPlaceholderText('Name');
    const descriptionInput = getByPlaceholderText('Description');
    const addButton = getByText('Add Data');

    fireEvent.changeText(nameInput, 'New Name');
    fireEvent.changeText(descriptionInput, 'New Description');
    fireEvent.press(addButton);

    await waitFor(() => {
      expect(getByText('New Name')).toBeTruthy();
      expect(getByText('New Description')).toBeTruthy();
    });
  });
});

Updating the HomeScreen Component

Add test identifiers to the HomeScreen.tsx component to make it easier to select elements during tests:

// src/HomeScreen.tsx

import React, { useState } from 'react';
import { View, Text, TextInput, Button, ActivityIndicator, FlatList } from 'react-native';
import { useQuery, useMutation } from '@apollo/client';
import { GET_DATA } from './queries';
import { ADD_DATA } from './mutations';

interface DataItem {
  id: string;
  name: string;
  description: string;
}

interface GetDataResult {
  data: DataItem[];
}

interface AddDataVars {
  name: string;
  description: string;
}

const HomeScreen: React.FC = () => {
  const { loading, error, data } = useQuery<GetDataResult>(GET_DATA);
  const [addData] = useMutation<DataItem, AddDataVars>(ADD_DATA);

  const [name, setName] = useState('');
  const [description, setDescription] = useState('');

  const handleAddData = () => {
    addData({ 
      variables: { name, description },
      refetchQueries: [{ query: GET_DATA }]
    });
  };

  if (loading) return <ActivityIndicator testID="loading" size="large" color="#0000ff" />;
  if (error) return <Text>Error: {error.message}</Text>;

  return (
    <View>
      <TextInput 
        placeholder="Name"
        value={name}
        onChangeText={setName}
        testID="name-input"
      />
      <TextInput 
        placeholder="Description"
        value={description}
        onChangeText={setDescription}
        testID="description-input"
      />
      <Button title="Add Data" onPress={handleAddData} testID="add-button" />

      <FlatList
        data={data?.data}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View>
            <Text>{item.name}</Text>
            <Text>{item.description}</Text>
          </View>
        )}
      />
    </View>
  );
};

export default HomeScreen;

Running the Tests

Now, you can run the tests using the command:

npm test

Conclusion

Adding tests to your React Native project with GraphQL and TypeScript is a crucial step to ensure the quality and robustness of your application. With the provided configurations and examples, you are well on your way to creating a comprehensive test suite for your application.


This content originally appeared on DEV Community and was authored by Paulo Messias


Print Share Comment Cite Upload Translate Updates
APA

Paulo Messias | Sciencx (2024-06-29T14:16:57+00:00) Integrating React Native with GraphQL: A Comprehensive Guide. Retrieved from https://www.scien.cx/2024/06/29/integrating-react-native-with-graphql-a-comprehensive-guide/

MLA
" » Integrating React Native with GraphQL: A Comprehensive Guide." Paulo Messias | Sciencx - Saturday June 29, 2024, https://www.scien.cx/2024/06/29/integrating-react-native-with-graphql-a-comprehensive-guide/
HARVARD
Paulo Messias | Sciencx Saturday June 29, 2024 » Integrating React Native with GraphQL: A Comprehensive Guide., viewed ,<https://www.scien.cx/2024/06/29/integrating-react-native-with-graphql-a-comprehensive-guide/>
VANCOUVER
Paulo Messias | Sciencx - » Integrating React Native with GraphQL: A Comprehensive Guide. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/06/29/integrating-react-native-with-graphql-a-comprehensive-guide/
CHICAGO
" » Integrating React Native with GraphQL: A Comprehensive Guide." Paulo Messias | Sciencx - Accessed . https://www.scien.cx/2024/06/29/integrating-react-native-with-graphql-a-comprehensive-guide/
IEEE
" » Integrating React Native with GraphQL: A Comprehensive Guide." Paulo Messias | Sciencx [Online]. Available: https://www.scien.cx/2024/06/29/integrating-react-native-with-graphql-a-comprehensive-guide/. [Accessed: ]
rf:citation
» Integrating React Native with GraphQL: A Comprehensive Guide | Paulo Messias | Sciencx | https://www.scien.cx/2024/06/29/integrating-react-native-with-graphql-a-comprehensive-guide/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.