Creating Custom Hooks in React Native

Custom hooks are a powerful feature in React and React Native that allows you to extract component logic into reusable functions. They help keep your code DRY (Don't Repeat Yourself)

· 3 min read
Cliff Gor

Cliff Gor

As a Full stack Software Engineer, I design and develop user-centric and robust systems and applications using NodeJS, Tailwind, React Js, React Native, and Kotlin(Android).

Custom hooks are a powerful feature in React and React Native that allows you to extract component logic into reusable functions. They help keep your code DRY (Don't Repeat Yourself) and make it easier to share logic between components. In this article, we'll explore how to create and use custom hooks in React Native applications.

Understanding Custom Hooks

Custom hooks are JavaScript functions that start with the word "use" and may call other hooks. They allow you to reuse stateful logic without changing your component hierarchy.

Basic Custom Hook: useToggle

Let's start with a simple custom hook that toggles a boolean value:

import { useState, useCallback } from 'react';

const useToggle = (initialState = false) => {
  const [state, setState] = useState(initialState);
  const toggle = useCallback(() => setState(state => !state), []);
  return [state, toggle];
};

This hook can be used in a component like this:

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

const ToggleComponent = () => {
  const [isOn, toggleIsOn] = useToggle();

  return (
    <View>
      <Text>{isOn ? 'ON' : 'OFF'}</Text>
      <Button title="Toggle" onPress={toggleIsOn} />
    </View>
  );
};

Custom Hook for API Calls: useFetch

Now, let's create a more complex custom hook for making API calls:

import { useState, useEffect } from 'react';

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
        setLoading(false);
      } catch (error) {
        setError(error);
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

You can use this hook in a component like this:

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

const UserList = () => {
  const { data, loading, error } = useFetch('https://api.example.com/users');

  if (loading) return <Text>Loading...</Text>;
  if (error) return <Text>Error: {error.message}</Text>;

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

Custom Hook for Form Handling: useForm

Form handling is a common task in React Native apps. Let's create a custom hook to simplify it:

import { useState } from 'react';

const useForm = (initialValues) => {
  const [values, setValues] = useState(initialValues);

  const handleChange = (name, value) => {
    setValues(prevValues => ({
      ...prevValues,
      [name]: value
    }));
  };

  const resetForm = () => {
    setValues(initialValues);
  };

  return [values, handleChange, resetForm];
};

Here's how you can use this hook in a login form:

import React from 'react';
import { View, TextInput, Button } from 'react-native';

const LoginForm = () => {
  const [formValues, handleChange, resetForm] = useForm({ username: '', password: '' });

  const handleSubmit = () => {
    console.log('Form submitted:', formValues);
    resetForm();
  };

  return (
    <View>
      <TextInput
        value={formValues.username}
        onChangeText={(text) => handleChange('username', text)}
        placeholder="Username"
      />
      <TextInput
        value={formValues.password}
        onChangeText={(text) => handleChange('password', text)}
        placeholder="Password"
        secureTextEntry
      />
      <Button title="Login" onPress={handleSubmit} />
    </View>
  );
};

Custom Hook for Animation: useAnimation

Animations are crucial for creating engaging user interfaces. Let's create a custom hook for a fade-in animation:

import { useRef, useEffect } from 'react';
import { Animated } from 'react-native';

const useFadeIn = (duration = 1000) => {
  const fadeAnim = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: duration,
      useNativeDriver: true,
    }).start();
  }, [fadeAnim, duration]);

  return fadeAnim;
};

You can use this hook to create a fading-in component:

import React from 'react';
import { Animated, Text } from 'react-native';

const FadingText = ({ text }) => {
  const fadeAnim = useFadeIn(1500);

  return (
    <Animated.View style={{ opacity: fadeAnim }}>
      <Text>{text}</Text>
    </Animated.View>
  );
};

Custom Hook for Device Orientation: useOrientation

Let's create a hook that detects device orientation changes:

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

const useOrientation = () => {
  const [orientation, setOrientation] = useState('PORTRAIT');

  useEffect(() => {
    const updateOrientation = () => {
      const { width, height } = Dimensions.get('window');
      setOrientation(width > height ? 'LANDSCAPE' : 'PORTRAIT');
    };

    const subscription = Dimensions.addEventListener('change', updateOrientation);
    updateOrientation();

    return () => subscription?.remove();
  }, []);

  return orientation;
};

You can use this hook to adjust your layout based on the device orientation:

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

const OrientationAwareComponent = () => {
  const orientation = useOrientation();

  return (
    <View style={orientation === 'LANDSCAPE' ? styles.landscape : styles.portrait}>
      <Text>Current Orientation: {orientation}</Text>
    </View>
  );
};

const styles = StyleSheet.css({
  landscape: {
    flexDirection: 'row',
    // other landscape styles
  },
  portrait: {
    flexDirection: 'column',
    // other portrait styles
  },
});

Conclusion

  • Start with "use": Always start your custom hook names with "use" to follow the React convention.
  • Keep it simple: Custom hooks should do one thing and do it well. If a hook becomes too complex, consider breaking it into smaller hooks.
  • Use built-in hooks: Leverage React's built-in hooks like useState, useEffect, useCallback, and useMemo within your custom hooks.
    Handle cleanup: If your hook sets up any subscriptions or timers, make sure to clean them up to prevent memory leaks.
  • Test your hooks: Write unit tests for your custom hooks to ensure they behave correctly in different scenarios.

Custom hooks are a powerful tool in React Native development. They allow you to extract and reuse logic across components, leading to cleaner and more maintainable code. By creating custom hooks for common patterns like API calls, form handling, animations, and device-specific features, you can significantly streamline your development process.

As you continue to work with React Native, look for opportunities to create custom hooks. They can help you solve complex problems, improve code organization, and make your codebase more modular and easier to understand.

Remember, the key to writing effective custom hooks is to focus on reusability and simplicity. Happy coding!

share

Cliff Gor

As a Fullstack Software Engineer, I design and develop user-centric and robust systems and applications using HTML, CSS, Bootstrap, React Js, React Native, and Kotlin(Android).

Related