DEV Community

Amit Kumar
Amit Kumar

Posted on

Understanding Higher-Order Components (HOCs) in React Native with a Custom Modal Example

Introduction

In React development, Higher-Order Components (HOCs) are a useful design pattern that allow you to reuse component functionality without altering the component itself. They help to keep components clean and focused, enabling developers to enhance their logic with added behaviors like state management or lifecycle handling.

Key Points:

  • What is an HOC?

  • An HOC is a pattern where a function takes a component and returns a new component with added behavior.

  • Commonly used to handle logic such as state management, UI enhancement, or lifecycle methods.

  • Why use HOCs?

  • Reusability: Write logic once and apply it across multiple components.

  • Separation of concerns: Keep component logic isolated from UI details, making code more modular and easier to maintain.

CustomModal Example

Let's take a look at a reusable modal component called CustomModal. Below is its basic structure:

import React from 'react';
import { Modal, View } from 'react-native';
import styles from './styles';

const CustomModal = ({
  isModalVisible = false,
  onClosePress = () => {},
  children,
  modalContainerStyle,
  modalStyle,
  onShow = () => {},
  onAndroidBackPress = () => {},
  animationType = 'fade',
}) => {
  return (
    <Modal
      animationType={animationType}
      supportedOrientations={['portrait', 'landscape']}
      transparent={true}
      visible={isModalVisible}
      onRequestClose={() => {
        onClosePress();
        onAndroidBackPress();
      }}
      onShow={onShow}
    >
      <View style={[styles.modalContainer, modalContainerStyle]}>
        <View style={[styles.modalView, modalStyle]}>{children}</View>
      </View>
    </Modal>
  );
};

export default CustomModal;

Enter fullscreen mode Exit fullscreen mode

Here’s the styles object for CustomModal, which you can import or place directly inside the component file:

const styles = StyleSheet.create({
  modalContainer: {
    flex: 1,
    backgroundColor: 'rgba(0, 0, 0, 0.5)', // Semi-transparent background
    justifyContent: 'center',
    alignItems: 'center',
  },
  modalView: {
    backgroundColor: '#FFFFFF',
    borderRadius: 16,
    paddingVertical: 24,
    paddingHorizontal: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.25,
    shadowRadius: 4,
    elevation: 5,
    width: '80%', // Set modal width to 80% of the screen width
  },
  button: {
    borderRadius: 20,
    padding: 10,
    elevation: 2,
  },
  buttonOpen: {
    backgroundColor: '#F194FF',
  },
  buttonClose: {
    backgroundColor: '#2196F3',
  },
  textStyle: {
    color: '#FFFFFF',
    fontWeight: 'bold',
    textAlign: 'center',
  },
  container: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginTop: 10,
  },
});

Enter fullscreen mode Exit fullscreen mode

This CustomModal component uses the Modal component from React Native and accepts various props like isModalVisible, onClosePress, and animationType, making it flexible for different modal use cases.

Creating a Specialized Modal: DeleteContentPickerModal

We can now create a more specific modal, such as DeleteContentPickerModal, by reusing CustomModal and adding customized behavior:

const DeleteContentPickerModal = ({
  isDeleteContentPickerModal = false,
  onClosePress = () => {},
  onRemoveButtonPress = () => {},
  contentId = '',
}) => {
  return (
    <CustomModal
      isModalVisible={isDeleteContentPickerModal}
      onClosePress={onClosePress}
      modalStyle={styles.modalStyle}
    >
      <View style={styles.container}>
        <Button
          text={'Remove From Download'}
          textStyle={styles.endButtonTextStyle}
          containerStyle={styles.endButtonContainerStyle}
          onPress={onRemoveButtonPress}
        />
        <Button
          text={'Cancel'}
          textStyle={styles.deleteTextStyle}
          containerStyle={styles.endButtonContainerStyle}
          onPress={onClosePress}
        />
      </View>
    </CustomModal>
  );
};

Enter fullscreen mode Exit fullscreen mode

This specific modal, DeleteContentPickerModal, inherits all the base functionality of CustomModal and adds unique UI and logic tailored to a content deletion feature.

Using the Modal in the App

Now that we've created our DeleteContentPickerModal, let's see how to use it in a React Native component. We'll control its visibility using state and display it when triggered.

import React, { useState } from 'react';
import { View, Button } from 'react-native';
import DeleteContentPickerModal from './DeleteContentPickerModal'; // Import your modal

const MyComponent = () => {
  const [isDeleteContentPickerModal, setDeleteContentPickerModal] = useState(false); // Modal visibility state

  return (
    <View>
      {/* Button to trigger the modal */}
      <Button 
        title="Open Modal" 
        onPress={() => setDeleteContentPickerModal(true)} 
      />

      {/* DeleteContentPickerModal Usage */}
      <DeleteContentPickerModal
        isDeleteContentPickerModal={isDeleteContentPickerModal}
        contentId={'12345'}
        onClosePress={() => setDeleteContentPickerModal(false)}
        onRemoveButtonPress={() => {
          // Add logic to handle content removal here
          setDeleteContentPickerModal(false); // Close modal after removing content
        }}
      />
    </View>
  );
};

export default MyComponent;

Enter fullscreen mode Exit fullscreen mode

With this setup, the modal can now be opened and closed based on state, allowing for smooth interaction within the app.

Conclusion

HOCs offer a powerful way to enhance React Native components by making logic reusable and keeping concerns separated. In this article, we explored how a basic modal component (CustomModal) can be reused and extended to create a more specialized modal (DeleteContentPickerModal). This pattern helps developers write cleaner, more maintainable code, which is essential for scalable React Native applications.

Top comments (0)