SOLID in React Native - part 1
Tags: reactnative, solidprinciples, typescript, mobile
Published: 2024-01-15 13:45:04

The SOLID acronym refers to five rules in software design. Based on these rules we can create software that is better understood, more flexible, and easier to maintain. In this post, we will introduce the first of these principles - the Single Responsability Principle, applied to mobile development with React Native, but equally applicable to any React application.

> Single Responsibility Principle (SRP) - "A class ought to serve a single, clearly defined purpose, reducing the need for frequent changes."

Let's take a look at the ListRepos component below and analyze what it's doing:

```
import React, { useEffect, useState } from 'react';
import { View, Text, Alert, ActivityIndicator } from 'react-native';

import axios from 'axios';

type Repo = {
  id: string;
  language: string;
  name: string;
};

export const ListRepos = () => {
  const [repos, setRepos] = useState<Array<Repo>>([]);
  const [isLoading, setIsLoading] = useState<boolean>(true);

  useEffect(() => {
    (async () => {
      try {
        const { data } = await axios.get(
          'https://api.github.com/users/davi1985/repos'
        );

        setRepos(data);
      } catch (error) {
        Alert.alert('Something wrong!');
      } finally {
        setIsLoading(false);
      }
    })();
  }, []);

  if (isLoading) {
    return <ActivityIndicator size={'large'} color={'#4285F4'} />;
  }

  return (
    <View>
      {repos.map((item) => (
        <View key={item.id}>
          <Text>{item.name}</Text>
          <Text>{item.language}</Text>
        </View>
      ))}
    </View>
  );
};
```
We can observe that the component is:

- performing data fetching
- rendering the list of repositories, and
- managing states

Thinking about SOLID, specifically the Single Responsibility Principle (SRP), we can separate responsibilities. First of all, let's isolate the data fetching part into a hook:

```
import { useState, useEffect } from 'react';
import { Alert } from 'react-native';

import axios from 'axios';

type Repo = {
  id: string;
  language: string;
  name: string;
};

export const useGetRepos = () => {
  const [repos, setRepos] = useState<Array<Repo>>([]);
  const [isLoading, setIsLoading] = useState<boolean>(true);

  useEffect(() => {
    (async () => {
      try {
        const { data } = await axios.get(
          'https://api.github.com/users/davi1985/repos'
        );

        setRepos(data);
      } catch (error) {
        Alert.alert('Something wrong!');
      } finally {
        setIsLoading(false);
      }
    })();
  }, []);

  return { isLoading, repos };
};
```
Now, our ListRepos component is cleaner and has fewer responsibilities: take a look:

```
import React from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
import { useGetRepos } from './useGetRepos';

export const ListRepos = () => {
  const { isLoading, repos } = useGetRepos();

  if (isLoading) {
    return <ActivityIndicator size={'large'} color={'#4285F4'} />;
  }

  return (
    <View>
      {repos.map((item) => (
        <View key={item.id}>
          <Text>{item.name}</Text>
          <Text>{item.language}</Text>
        </View>
      ))}
    </View>
  );
};
```
We can make it even better. If we observe, each item in our list is being rendered directly in our main component. We can separate it and pass what is necessary for its rendering via props. Take a look:

```
import { Text, View } from 'react-native';

type Props = {
  id: string;
  language: string;
  name: string;
};

export const Repo = ({ id, language, name }: Props) => (
  <View key={id}>
    <Text>{name}</Text>
    <Text>{language}</Text>
  </View>
);
```
With our Repo component created, our ListRepos component becomes simpler:

```
import React from 'react';
import { View, ActivityIndicator } from 'react-native';
import { useGetRepos } from './useGetRepos';
import { Repo } from './Repo';

export const ListRepos = () => {
  const { isLoading, repos } = useGetRepos();

  if (isLoading) {
    return <ActivityIndicator size={'large'} color={'#4285F4'} />;
  }

  return (
    <View>
      {repos.map((item) => (
        <Repo {...item} />
      ))}
    </View>
  );
};
```
We can keep improving. I'll leave a few challenges for you to implement and elevate your understanding of SRP:

- Isolate the Axios call in another file and only call that functions in the useGetRerpos hook
- The typing for the Repo component is the same as the one used in the hook, meaning it's repetitive. We can create a @types folder to share types across the application.

By implementing the Single Responsibility Principle (SRP) in our React Native development, we have achieved a more cohesive and modular structure. In the next post, we'll explore another principle to make our software design even better.