Building a TikTok-Like Video Streaming App in React Native

Building a TikTok-Like Video Streaming App in React Native

Are you eager to create your video streaming app, just like TikTok, using the power of React Native? Look no further! In this blog, we'll dive into the exciting world of mobile app development and explore how to build a captivating video streaming platform with Flatlist. Harnessing the versatility of React Native, you'll be able to provide users with a seamless and engaging video-sharing experience.

Introduction

Video streaming apps have taken the digital world by storm, captivating millions of users worldwide. By the end of this tutorial, you'll have the essential knowledge and skills to embark on your journey to create a TikTok-like app, complete with smooth video playback and dynamic content feeds. So, let's get started and bring your vision of a viral video platform to life!
So, let's roll up our sleeves, dive into the code, and witness the magic of Flatlist and React Native as we craft an engaging video streaming app that'll keep users coming back for more!

Before we delve into the development process, ensure you have the following dependencies installed in your project:

Install Libraries

  1. Axios: Axios is a powerful HTTP client that will simplify your data fetching and communication with the backend.

  2. React Native Safe Area Context: This library provides a safe area context for your React Native application, ensuring that your app's UI elements are correctly positioned within the safe area of the device's screen.

  3. React Native Video: As the name suggests, this library empowers your app to handle video playback effortlessly.

To add these libraries to your project, use the following command:

yarn add react-native-video react-native-safe-area-context axios

Once you have successfully installed these dependencies, you'll be well-equipped to create a seamless video streaming experience.

Below is the heart of your React Native application, where the magic begins. Here, we import the necessary modules and components to ensure a smooth and engaging video streaming experience. Let's take a closer look at what's happening here:

import React from 'react';
import {SafeAreaProvider, SafeAreaView} from 'react-native-safe-area-context';

import Streaming from './src/screens/Streaming';

export default function App() {
  return (
    <SafeAreaProvider>
      <SafeAreaView style={{flex: 1}}>
        <Streaming />
      </SafeAreaView>
    </SafeAreaProvider>
  );
}

The SafeAreaProvider and SafeAreaView are imported from 'react-native-safe-area-context'. These components will handle safe area insets for different devices, ensuring that your app's UI elements are well-positioned on the screen.

Within the 'SafeAreaProvider', we further wrap the 'Streaming' component with the 'SafeAreaView'. This ensures that your video streaming content is correctly positioned within the safe area of the device.

The 'Streaming' component is imported from './src/screens/Streaming', which holds the logic and presentation of your video streaming screen. You can customize and enhance this component to add more features.

In this part of the application, we're diving deep into the video streaming experience. The 'Streaming' component is where the magic happens. Let's explore what's happening in this code:

import React, {Component} from 'react';
import {View, StyleSheet, ActivityIndicator, FlatList} from 'react-native';

import axios from 'axios';

import VideoView from '../components/VideoView';

export default class Streaming extends Component {
  constructor(props) {
    super(props);
    this.state = {
      muxResponse: null,
      lastIndex: 0,
      page: 0,
      isRefreshing: false,
      itemHeight: 0,
    };

    this.onViewableItemsChanged = ({viewableItems, changed}) => {
      const visibleIndex = viewableItems[0]?.index;
      const previousIndex = changed[1]?.index;
      if (visibleIndex !== undefined) {
        this.visibleRef[`REF-FLATLIST${visibleIndex}`]?.playThisVideo(true);
        this.setState({
          lastIndex: visibleIndex,
        });
      }
      if (previousIndex !== undefined) {
        this.visibleRef[`REF-FLATLIST${previousIndex}`]?.playThisVideo(false);
      }
    };
    this.viewabilityConfig = {
      itemVisiblePercentThreshold: 50,
    };
  }

  componentDidMount() {
    this.fetchHomeFeed(1, false);
  }

  refreshStreamData() {
    this.setState({
      isRefreshing: true,
    });
    this.fetchHomeFeed(1, true);
  }

  async getVideos(page, muxResp, refreshing) {
    const url = `https://xyz.co/home-feed?page=${page}`;
    //replace above url with your working url
    try {
      const res = await axios.get(url);
      if (muxResp && !refreshing) {
        const {data} = res;
        data.docs = muxResp?.docs.concat(res.data?.docs);
        this.setState({
          muxResponse: data,
        });
      } else {
        this.setState({
          muxResponse: res?.data,
        });
      }
      this.setState({
        page: parseInt(res.data?.page, 10) + 1,
        loading: false,
        isRefreshing: false,
      });
      return true;
    } catch (error) {
      this.setState({
        loading: false,
        isRefreshing: false,
      });
      return false;
    }
  }

  async fetchHomeFeed(page, refreshing) {
    const {muxResponse} = this.state;
    await this.getVideos(page, muxResponse, refreshing);
  }

  render() {
    const {muxResponse, page, isRefreshing, itemHeight} = this.state;

    if (!muxResponse) {
      return (
        <View style={styles.flex}>
          <ActivityIndicator size={20} color="#000" style={styles.loader} />
        </View>
      );
    }

    return (
      <View style={styles.flex}>
        <FlatList
          onLayout={e => {
            this.setState({
              itemHeight: e.nativeEvent.layout.height,
            });
          }}
          contentContainerStyle={{flexGrow: 1}}
          data={muxResponse?.docs}
          onViewableItemsChanged={this.onViewableItemsChanged}
          viewabilityConfig={this.viewabilityConfig}
          removeClippedSubviews
          keyExtractor={item => item?.muxPlaybackId}
          pagingEnabled
          initialNumToRender={1}
          maxToRenderPerBatch={6}
          updateCellsBatchingPeriod={30}
          windowSize={4}
          decelerationRate="fast"
          showsVerticalScrollIndicator={false}
          onEndReachedThreshold={1.9}
          onEndReached={() => {
            this.fetchHomeFeed(page, false);
          }}
          refreshing={isRefreshing}
          onRefresh={() => this.refreshStreamData()}
          renderItem={({item, index}) => (
            <View
              style={{
                height: itemHeight,
                width: '100%',
              }}>
              <VideoView
                ref={ref => {
                  this.visibleRef = {
                    ...this.visibleRef,
                    [`REF-FLATLIST${index}`]: ref,
                  };
                }}
                item={item}
                index={index}
              />
            </View>
          )}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  flex: {
    flex: 1,
  },
  loader: {
    marginTop: '50%',
  },
});

The 'Streaming' class is a React component that takes care of fetching and displaying the video feed from the backend API. It relies on Axios, a powerful HTTP client, to make API requests and retrieve video data from the server.

  • The 'muxResponse' state holds the fetched video data, and 'page' represents the current page number to enable pagination. The 'isRefreshing' state is used to show the ActivityIndicator when new videos are being fetched upon refreshing the feed. The 'itemHeight' state ensures that each video item within the FlatList has the same height, offering a seamless scrolling experience.

  • The 'FlatList' component displays the video feed using the 'muxResponse' data. It optimizes performance by rendering only visible items and handles pagination when the user scrolls to the end. The 'VideoView' component is imported from '../components/VideoView'. It plays videos with smooth transitions and ensures the active video is focused while others are paused.

So, grab a cup of coffee, and let's continue building your TikTok-Like Video Streaming App! Happy coding!

The 'VideoView' component is a crucial piece of your application, responsible for rendering individual videos within the FlatList on the 'Streaming' screen. Let's take a closer look at what this component does:

The component's state includes 'pause', which determines whether the video should be paused or played based on user actions and visibility within the FlatList. The 'shouldComponentUpdate' method optimizes rendering performance by updating only when the 'pause' state changes. The 'playThisVideo' method allows controlling video playback by updating the 'pause' state based on user interactions and visibility in the FlatList. The video's URL is dynamically generated using the 'muxPlaybackId' provided in the 'item' prop, allowing the 'VideoView' component to play different videos as users scroll through the FlatList.

Additional video settings, such as poster image, buffering configurations, playback options, and error handling, are fine-tuned to enhance the overall video streaming experience.

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

import Video from 'react-native-video';

export default class VideoView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      pause: true,
    };

    const {index, item} = this.props;

    this.index = index;
    this.item = item;
  }

  shouldComponentUpdate(pro, sta) {
    const {pause} = this.state;
    if (sta?.pause !== pause) {
      return true;
    }
    return false;
  }

  playThisVideo(play) {
    this.setState({
      pause: !play,
    });
  }

  render() {
    const {item} = this;
    const {pause} = this.state;

    const url = `https://stream.mux.com/${item?.muxPlaybackId}.m3u8`;

    return (
      <View style={styles.videoWrapper}>
        <Video
          style={styles.videoWrapper}
          source={{
            uri: url,
            headers: {
              range: 'bytes=0-',
            },
          }}
          reportBandwidth
          rate={1}
          volume={1}
          muted={false}
          // paused
          paused={pause}
          poster={`https://image.mux.com/${item?.muxPlaybackId}/thumbnail.webp?width=250&height=350&fit_mode=smartcrop`}
          posterResizeMode="cover"
          useTextureView={false}
          playInBackground
          disableFocus
          playWhenInactive={false}
          ignoreSilentSwitch="ignore"
          onError={e => {
            console.error(e);
            return false;
          }}
          onBuffer={() => {
            // console.log('buffer', buffer)
          }}
          progressUpdateInterval={2500}
          resizeMode="cover"
          repeat
          allowsExternalPlayback
          automaticallyWaitsToMinimizeStalling={false}
          bufferConfig={{
            minBufferMs: 15000,
            maxBufferMs: 50000,
            bufferForPlaybackMs: 2500,
            bufferForPlaybackAfterRebufferMs: 5000,
          }}
          minLoadRetryCount={5}
          selectedVideoTrack={{
            type: 'resolution',
            value: 480,
          }}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  videoWrapper: {
    width: '100%',
    height: '100%',
  },
});

With the 'VideoView' component, you can ensure that each video within your app is beautifully displayed, effortlessly played, and seamlessly transitioned as users interact with your TikTok-Like Video Streaming App. By combining this component with the power of 'react-native-video' and 'react-native-safe-area-context', your app will provide a delightful video streaming journey for all its users.

Congratulations! You've now built the foundation of your TikTok-Like Video Streaming App using React Native, Flatlist, Axios, and other powerful libraries. You've created the 'App' component, the 'Streaming' screen, and the 'VideoView' component, all working together harmoniously to deliver an immersive video-sharing experience to your users. But don't stop here! There's so much more you can do to enhance your app and make it stand out. Why not add some exciting features to make your app truly one-of-a-kind? Here are some ideas to get you started:

  1. User Profiles and Authentication: Implement user authentication and create user profiles, allowing users to like, comment, and share videos, fostering a sense of community within your app.

  2. Video Upload and Editing: Enable users to upload their videos directly from their devices. You can also explore video editing features, such as filters and effects, to add a touch of creativity to the content.

  3. Social Sharing: Integrate social media sharing functionalities, allowing users to share their favourite videos on platforms like Instagram, Facebook, or Twitter.

  4. Trending Hashtags and Discover Feed: Create a trending hashtags section and a personalized Discover feed, showcasing popular videos and helping users explore new content.

  5. Real-Time Notifications: Implement real-time notifications to keep users updated on new likes, comments, and follows, increasing engagement and user retention.

Did you find this article valuable?

Support Fiyaz Hussain by becoming a sponsor. Any amount is appreciated!