<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alberto Cabrera</title>
    <description>The latest articles on Forem by Alberto Cabrera (@albertocabrerajr).</description>
    <link>https://forem.com/albertocabrerajr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1230223%2F94cafea3-2341-41ff-ad59-7ba71d93dbfe.jpeg</url>
      <title>Forem: Alberto Cabrera</title>
      <link>https://forem.com/albertocabrerajr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/albertocabrerajr"/>
    <language>en</language>
    <item>
      <title>SOLID Principles for React / React Native Development</title>
      <dc:creator>Alberto Cabrera</dc:creator>
      <pubDate>Fri, 03 Jan 2025 17:34:56 +0000</pubDate>
      <link>https://forem.com/albertocabrerajr/solid-principles-for-react-react-native-development-26l</link>
      <guid>https://forem.com/albertocabrerajr/solid-principles-for-react-react-native-development-26l</guid>
      <description>&lt;p&gt;In &lt;strong&gt;React development&lt;/strong&gt;, creating components that are &lt;strong&gt;scalable, maintainable&lt;/strong&gt;, and &lt;strong&gt;easy to extend&lt;/strong&gt; is a fundamental goal. While the &lt;strong&gt;SOLID principles&lt;/strong&gt; were initially designed for &lt;strong&gt;object-oriented programming by Robert C. Martin (Uncle Bob)&lt;/strong&gt;, they offer valuable guidelines that can also be effectively applied to &lt;strong&gt;React projects.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the SOLID Principles?
&lt;/h2&gt;

&lt;p&gt;The SOLID principles are five design principles that aim to make software easier to understand, maintain, and extend. They are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S&lt;/strong&gt; – Single Responsibility Principle&lt;br&gt;
&lt;strong&gt;O&lt;/strong&gt; – Open/Closed Principle&lt;br&gt;
&lt;strong&gt;L&lt;/strong&gt; – Liskov Substitution Principle&lt;br&gt;
&lt;strong&gt;I&lt;/strong&gt; – Interface Segregation Principle&lt;br&gt;
&lt;strong&gt;D&lt;/strong&gt;– Dependency Inversion Principle&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Single Responsibility Principle (SRP)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“A component should have only one reason to change.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In React, this means a component or hook should handle one responsibility. Mixing UI logic with business logic makes components hard to maintain.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Anti-Pattern Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function UserProfile() {
  const [user, setUser] = useState(null);

  useEffect(() =&amp;gt; {
    fetch('/api/user').then(res =&amp;gt; res.json()).then(setUser);
  }, []);

  return &amp;lt;div&amp;gt;{user?.name}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UserProfile component is responsible for both fetching user data and displaying it.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Better Approach: Separate Concerns&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function useUserData() {
  const [user, setUser] = useState(null);

  useEffect(() =&amp;gt; {
    fetch('/api/user').then(res =&amp;gt; res.json()).then(setUser);
  }, []);

  return user;
}

function UserProfile() {
  const user = useUserData();

  return &amp;lt;div&amp;gt;{user?.name}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 &lt;strong&gt;Takeaway:&lt;/strong&gt; Separate data-fetching logic into a custom hook (useUserData) and keep the component focused on rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Open/Closed Principle (OCP)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“Components should be open for extension but closed for modification.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This means we should be able to extend the functionality of a component without modifying its existing code.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Anti-Pattern Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Button({ type }) {
  if (type === 'primary') return &amp;lt;button style={{ color: 'blue' }}&amp;gt;Primary&amp;lt;/button&amp;gt;;

  if (type === 'secondary') return &amp;lt;button style={{ color: 'gray' }}&amp;gt;Secondary&amp;lt;/button&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding more button types will require modifying the component, violating OCP.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Better Approach: Use Props for Flexibility&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function Button({ children, style }) {
  return &amp;lt;button style={style}&amp;gt;{children}&amp;lt;/button&amp;gt;;
}

// Usage:
&amp;lt;Button style={{ color: 'blue' }}&amp;gt;Primary&amp;lt;/Button&amp;gt;
&amp;lt;Button style={{ color: 'gray' }}&amp;gt;Secondary&amp;lt;/Button
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 &lt;strong&gt;Takeaway:&lt;/strong&gt; Use props and composition to extend behavior without changing the core logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Liskov Substitution Principle (LSP)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“Child components should behave predictably when used in place of their parent components.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When passing props, ensure they don’t introduce unexpected behavior.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Anti-Pattern Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function InputField({ value }) {
  if (typeof value !== 'string') throw new Error('Value must be a string');
  return &amp;lt;input value={value} /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This component expects strict behavior, making it less reusable.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Better Approach: Add Default Values&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function InputField({ value = '' }) {
  return &amp;lt;input value={value} /&amp;gt;;
}

// Usage:
&amp;lt;InputField value="Hello" /&amp;gt;
&amp;lt;InputField /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 &lt;strong&gt;Takeaway:&lt;/strong&gt; Components should gracefully handle various scenarios without breaking behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Interface Segregation Principle (ISP)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“Components should not be forced to accept props they do not need.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This principle suggests keeping your props clean and focused.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Anti-Pattern Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function UserCard({ name, age, isAdmin }) {
  return &amp;lt;div&amp;gt;{name}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UserCard component accepts more props than it uses.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Better Approach: Split Responsibilities&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function UserName({ name }) {
  return &amp;lt;div&amp;gt;{name}&amp;lt;/div&amp;gt;;
}

function UserAdmin({ isAdmin }) {
  return isAdmin ? &amp;lt;div&amp;gt;Admin&amp;lt;/div&amp;gt; : null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 &lt;strong&gt;Takeaway:&lt;/strong&gt; Avoid overloaded props. Create smaller components to handle specific responsibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Dependency Inversion Principle (DIP)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;“High-level modules should not depend on low-level modules. Both should depend on abstractions.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In React, use custom hooks or context to abstract dependencies.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Anti-Pattern Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function UserProfile() {
  const [user, setUser] = useState(null);

  useEffect(() =&amp;gt; {
    fetch('/api/user').then(res =&amp;gt; res.json()).then(setUser);
  }, []);

  return &amp;lt;div&amp;gt;{user?.name}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component directly depends on the fetch API.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Better Approach: Use Dependency Injection&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function useUserService(fetcher) {
  const [user, setUser] = useState(null);

  useEffect(() =&amp;gt; {
    fetcher().then(setUser);
  }, [fetcher]);

  return user;
}

// Usage:
function UserProfile() {
  const user = useUserService(() =&amp;gt; fetch('/api/user').then(res =&amp;gt; res.json()));
  return &amp;lt;div&amp;gt;{user?.name}&amp;lt;/div&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 &lt;strong&gt;Takeaway:&lt;/strong&gt; Abstract dependencies via hooks to make the code easier to test and maintain.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;🎯 Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Principle&lt;/th&gt;
&lt;th&gt;Focus in React&lt;/th&gt;
&lt;th&gt;Best Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;S&lt;/strong&gt; – Single Responsibility&lt;/td&gt;
&lt;td&gt;Components &amp;amp; Hooks&lt;/td&gt;
&lt;td&gt;Keep logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;O&lt;/strong&gt; – Open/Closed&lt;/td&gt;
&lt;td&gt;Props &amp;amp; Composition&lt;/td&gt;
&lt;td&gt;Extend via props, avoid modification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;L&lt;/strong&gt; – Liskov Substitution&lt;/td&gt;
&lt;td&gt;Props &amp;amp; Behavior&lt;/td&gt;
&lt;td&gt;Ensure predictable behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;I&lt;/strong&gt; – Interface Segregation&lt;/td&gt;
&lt;td&gt;Props&lt;/td&gt;
&lt;td&gt;Use focused, minimal props&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;D&lt;/strong&gt; – Dependency Inversion&lt;/td&gt;
&lt;td&gt;Hooks &amp;amp; Context&lt;/td&gt;
&lt;td&gt;Abstract dependencies via hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🛠️ &lt;strong&gt;Final Thoughts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Applying &lt;strong&gt;SOLID principles&lt;/strong&gt; in &lt;strong&gt;React&lt;/strong&gt; helps create cleaner, reusable, and maintainable components. By following these guidelines, you'll improve your code's scalability and make future changes easier to manage.&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactnative</category>
      <category>solidprinciples</category>
      <category>reactjsdevelopment</category>
    </item>
    <item>
      <title>Create a TikTok-like Scrolling Video Feed with React Native Expo</title>
      <dc:creator>Alberto Cabrera</dc:creator>
      <pubDate>Sun, 24 Dec 2023 18:23:34 +0000</pubDate>
      <link>https://forem.com/albertocabrerajr/implement-tiktok-like-feed-using-react-native-expo-1le2</link>
      <guid>https://forem.com/albertocabrerajr/implement-tiktok-like-feed-using-react-native-expo-1le2</guid>
      <description>&lt;p&gt;Before we dive into the article, let's preview a demonstration video of the Tiktok-esque video feed that we are about to build. The demo video shows a feed with smooth scrolling, auto-play and pause functionality. Pay attention to how videos automatically play when in focus and pause when not, and the full-screen playback for an immersive experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg64uugguz3h0r283a7nf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg64uugguz3h0r283a7nf.gif" alt="Demo-Application" width="264" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite:&lt;/strong&gt; Ensure that you have a React Native Expo project already set up. This is a fundamental requirement for following along with the tutorial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install expo-av&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;expo-av&lt;/code&gt; is an Expo module specifically designed for audio and video playback in React Native Expo apps. It's vital for our project because it allows for smooth video playback and control, ensuring an engaging user experience on both iOS and Android platforms. &lt;/p&gt;

&lt;p&gt;To set up, simply run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx expo install expo-av
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. FeedScreen component&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;You can name this component whatever you prefer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react'

const FeedScreen = () =&amp;gt; {
 return (
   &amp;lt;&amp;gt;
   &amp;lt;/&amp;gt;
 )
}

export default FeedScreen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Import Modules&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { View, Dimensions, FlatList, StyleSheet, Pressable } from 'react-native';
import { Video, ResizeMode } from 'expo-av';
import React, { useEffect, useRef, useState } from 'react';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Video&lt;/code&gt;, &lt;code&gt;ResizeMode&lt;/code&gt; from &lt;code&gt;expo-av&lt;/code&gt;:&lt;br&gt;
These will handle video playback. Video is the component for displaying videos, and ResizeMode controls how the video fits into its container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Video Data Array&lt;/strong&gt;&lt;br&gt;
You can copy the videos array below or copy from this list of public video urls &lt;a href="https://gist.github.com/jsturgis/3b19447b304616f18657" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const videos = [
 "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4",
 "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
 "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
 "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
 "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4",
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. State Management and Viewability&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [currentViewableItemIndex, setCurrentViewableItemIndex] = useState(0);
const viewabilityConfig = { viewAreaCoveragePercentThreshold: 50 }
const viewabilityConfigCallbackPairs = useRef([{ viewabilityConfig, onViewableItemsChanged }])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;currentViewableItemIndex&lt;/code&gt;&lt;br&gt;
Keeps track of which video is currently in the user's view. This is crucial for knowing which video to play.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;viewabilityConfig&lt;/code&gt;&lt;br&gt;
Defines what counts as a "viewable" item. Here, an item covering more than 50% of the screen is considered viewable. This threshold ensures that the video in the main focus of the screen is the one that plays.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;viewabilityConfigCallbackPairs&lt;/code&gt;&lt;br&gt;
Manages how videos in the feed are played and paused based on their visibility on the screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Viewable Items Change Handler&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const onViewableItemsChanged = ({ viewableItems }: any) =&amp;gt; {
    if (viewableItems.length &amp;gt; 0) {
      setCurrentViewableItemIndex(viewableItems[0].index ?? 0);
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function updates currentViewableItemIndex based on the item currently in view. It's essential for determining which video should be playing as the user scrolls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. FlatList for Rendering Videos&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;return (
    &amp;lt;View style={styles.container}&amp;gt;
      &amp;lt;FlatList
      data={videos}
      renderItem={({ item, index }) =&amp;gt; (
        &amp;lt;Item item={item} shouldPlay={index === currentViewableItemIndex} /&amp;gt;
      )}
      keyExtractor={item =&amp;gt; item}
      pagingEnabled
      horizontal={false}
      showsVerticalScrollIndicator={false}
      viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
    /&amp;gt;
    &amp;lt;/View&amp;gt;
  );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pagingEnabled&lt;/code&gt;&lt;br&gt;
When set to true, the list allows snapping to items (paging) as you scroll, creating a carousel-like effect. This is similar to how feeds work in social media apps.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;viewabilityConfigCallbackPairs&lt;/code&gt; &lt;br&gt;
This prop links our viewability configuration and callback function to the FlatList. It's crucial for detecting which video is in the viewport and should thus be playing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. The Item Component&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Item = ({ item, shouldPlay }: {shouldPlay: boolean; item: string}) =&amp;gt; {
  const video = React.useRef&amp;lt;Video | null&amp;gt;(null);
  const [status, setStatus] = useState&amp;lt;any&amp;gt;(null);

  useEffect(() =&amp;gt; {
    if (!video.current) return;

    if (shouldPlay) {
      video.current.playAsync()
    } else {
      video.current.pauseAsync()
      video.current.setPositionAsync(0)
    }
  }, [shouldPlay])

  return (
    &amp;lt;Pressable onPress={() =&amp;gt; status.isPlaying ? video.current?.pauseAsync() : video.current?.playAsync()}&amp;gt;
      &amp;lt;View style={styles.videoContainer}&amp;gt;
      &amp;lt;Video 
        ref={video}
        source={{ uri: item }}
        style={styles.video}
        isLooping
        resizeMode={ResizeMode.COVER}
        useNativeControls={false}
        onPlaybackStatusUpdate={status =&amp;gt; setStatus(() =&amp;gt; status)}
      /&amp;gt;
    &amp;lt;/View&amp;gt;
    &amp;lt;/Pressable&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Item component detailed breakdown&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const video = React.useRef&amp;lt;Video | null&amp;gt;(null);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reference allows you to control the video's playback programmatically (like playing or pausing). &lt;code&gt;useRef&lt;/code&gt; is used instead of a state variable because we need a way to persistently access the video component without causing re-renders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [status, setStatus] = useState&amp;lt;any&amp;gt;(null);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This state holds the playback status of the video (like whether it's playing, paused, buffering, etc.). It's updated whenever the playback status of the video changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;useEffect(() =&amp;gt; {
    if (!video.current) return;
    if (shouldPlay) {
        video.current.playAsync();
    } else {
        video.current.pauseAsync();
        video.current.setPositionAsync(0);
    }
}, [shouldPlay]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This useEffect is triggered whenever the shouldPlay prop changes. It checks if the video is supposed to be playing. If so, it starts playback; otherwise, it pauses the video and resets its position to the start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Pressable onPress={() =&amp;gt; status.isPlaying ? video.current?.pauseAsync() : video.current?.playAsync()}&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wraps the video component to make it interactive. When the user taps the video, it toggles between playing and pausing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Video 
    ref={video}
    source={{ uri: item }}
    style={styles.video}
    isLooping
    resizeMode={ResizeMode.COVER}
    useNativeControls={false}
    onPlaybackStatusUpdate={status =&amp;gt; setStatus(() =&amp;gt; status)}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;isLooping&lt;/code&gt;: If true, the video will loop continuously.&lt;br&gt;
&lt;code&gt;resizeMode&lt;/code&gt;: Determines how the video fits within the bounds of its container.&lt;br&gt;
&lt;code&gt;useNativeControls&lt;/code&gt;: Set to false to hide native playback controls.&lt;br&gt;
&lt;code&gt;onPlaybackStatusUpdate&lt;/code&gt;: A callback function that updates the status state whenever the playback status of the video changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Styles&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  videoContainer: {
    width: Dimensions.get('window').width,
    height: Dimensions.get('window').height,
  },
  video: {
    width: '100%',
    height: '100%',
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Full Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { View, Dimensions, FlatList, StyleSheet, Pressable } from 'react-native';
import { Video, ResizeMode } from 'expo-av';
import React, { useEffect, useRef, useState } from 'react';

const videos = [
  "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4",
  "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
  "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
  "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4",
  "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4",
];

export default function FeedScreen() {
  const [currentViewableItemIndex, setCurrentViewableItemIndex] = useState(0);
  const viewabilityConfig = { viewAreaCoveragePercentThreshold: 50 }
  const onViewableItemsChanged = ({ viewableItems }: any) =&amp;gt; {
    if (viewableItems.length &amp;gt; 0) {
      setCurrentViewableItemIndex(viewableItems[0].index ?? 0);
    }
  }
  const viewabilityConfigCallbackPairs = useRef([{ viewabilityConfig, onViewableItemsChanged }])
  return (
    &amp;lt;View style={styles.container}&amp;gt;
      &amp;lt;FlatList
      data={videos}
      renderItem={({ item, index }) =&amp;gt; (
        &amp;lt;Item item={item} shouldPlay={index === currentViewableItemIndex} /&amp;gt;
      )}
      keyExtractor={item =&amp;gt; item}
      pagingEnabled
      horizontal={false}
      showsVerticalScrollIndicator={false}
      viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
    /&amp;gt;
    &amp;lt;/View&amp;gt;
  );
}

const Item = ({ item, shouldPlay }: {shouldPlay: boolean; item: string}) =&amp;gt; {
  const video = React.useRef&amp;lt;Video | null&amp;gt;(null);
  const [status, setStatus] = useState&amp;lt;any&amp;gt;(null);

  useEffect(() =&amp;gt; {
    if (!video.current) return;

    if (shouldPlay) {
      video.current.playAsync()
    } else {
      video.current.pauseAsync()
      video.current.setPositionAsync(0)
    }
  }, [shouldPlay])

  return (
    &amp;lt;Pressable onPress={() =&amp;gt; status.isPlaying ? video.current?.pauseAsync() : video.current?.playAsync()}&amp;gt;
      &amp;lt;View style={styles.videoContainer}&amp;gt;
      &amp;lt;Video 
        ref={video}
        source={{ uri: item }}
        style={styles.video}
        isLooping
        resizeMode={ResizeMode.COVER}
        useNativeControls={false}
        onPlaybackStatusUpdate={status =&amp;gt; setStatus(() =&amp;gt; status)}
      /&amp;gt;
    &amp;lt;/View&amp;gt;
    &amp;lt;/Pressable&amp;gt;
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  videoContainer: {
    width: Dimensions.get('window').width,
    height: Dimensions.get('window').height,
  },
  video: {
    width: '100%',
    height: '100%',
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;That’s all! Happy coding 🚀&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>mobile</category>
      <category>react</category>
    </item>
    <item>
      <title>Implementing a CI/CD Deployment Pattern for React Native Expo Applications with Github Actions</title>
      <dc:creator>Alberto Cabrera</dc:creator>
      <pubDate>Sun, 10 Dec 2023 15:22:30 +0000</pubDate>
      <link>https://forem.com/albertocabrerajr/implementing-a-cicd-deployment-pattern-for-react-native-expo-applications-with-github-actions-39el</link>
      <guid>https://forem.com/albertocabrerajr/implementing-a-cicd-deployment-pattern-for-react-native-expo-applications-with-github-actions-39el</guid>
      <description>&lt;p&gt;Our deployment pattern leverages the Persistent Staging Flow directly from the &lt;a href="https://docs.expo.dev/eas-update/deployment-patterns/#persistent-staging-flow"&gt;Expo documentation&lt;/a&gt;. This flow is designed for continuous integration and delivery, with a focus on maintaining persistent "staging" and "production" branches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Aspects of the Deployment Pattern
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Creating Builds&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production Builds: Distinct builds tailored for the production environment.&lt;/li&gt;
&lt;li&gt;Testing Builds: Separate, dedicated builds for testing purposes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Testing Changes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement internal distribution builds for comprehensive pre-production testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Publishing Updates&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utilize designated branches, like "staging" and "production", for streamlined update management.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Application of the Pattern: A Practical Example
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Local Development&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Begin with a feature branch (e.g., feature/dark-mode) based on the main branch.&lt;/li&gt;
&lt;li&gt;Develop the feature with an emphasis on coding, UI design, and local testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Merging to Main&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After development, commit the changes and push to feature/dark-mode.&lt;/li&gt;
&lt;li&gt;Merge into the main branch upon pull request approval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Testing in Staging&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merge updates from the main to staging.&lt;/li&gt;
&lt;li&gt;Trigger a staging build through GitHub Actions, followed by internal distribution for testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Iterative Fixes and Updates&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Address feedback, creating and merging fixed branches as necessary.&lt;/li&gt;
&lt;li&gt;Conduct additional testing after merging updates into staging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Deployment to Production&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm feature stability in staging before merging to production.&lt;/li&gt;
&lt;li&gt;Update version numbers in app.json/app.config.js and initiate automated production deployment via GitHub Actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CI/CD Configuration Files
&lt;/h2&gt;

&lt;p&gt;Setup the different profiles in build and submit sections in eas.json that are needed. Here is an example,&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;eas.json&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "build": {
   "development": {
     "developmentClient": true,
     "distribution": "internal"
   },
   "preview": {
     "channel": "staging",
     "distribution": "internal",
     "env": {
       "EXPO_PUBLIC_API_HOST": // your staging api url
     }
     // ...other values
     }
   },
   "production": {
     "channel": "production"
     "env": {
       "EXPO_PUBLIC_API_HOST": // your production api url
     }
     // ...other values
   }
 },
 "submit": {
   "production": {
       // This is optional if you already setup the service account key in your expo account
     "android": {
       "serviceAccountKeyPath": "../path/to/api-xxx-yyy-zzz.json",
       "track": "internal"
     },
     "ios": {
       "appleId": "john@turtle.com",
       "ascAppId": "1234567890",
       "appleTeamId": "AB12XYZ34S"
     }
   }
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub Actions YAML Configurations&lt;/strong&gt;&lt;br&gt;
Set up your &lt;strong&gt;EXPO_TOKEN&lt;/strong&gt; as a secret in your GitHub repository.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;staging.yml&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
This job will create a staging build that you can distribute to your internal testers to test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Build For Staging
on:
  push:
    branches: ["staging"]
jobs:
  build:
    name: Build For Staging
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 16.x
          cache: npm
      - name: Setup Expo and EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}
      - name: Install dependencies
        run: npm ci
      - name: Build For Staging
        run: eas build --profile preview --platform all --non-interactive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;production.yml&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
This job will create a production build and submit this build to AppStore Connect and Google Play Console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: Build For Production
on:
  push:
    branches: ["production"]
jobs:
  build:
    name: Build For Production
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 16.x
          cache: npm
      - name: Setup Expo and EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}
      - name: Install dependencies
        run: npm ci
      - name: Build For Production
        run: eas build --profile production --platform all --non-interactive --auto-submit --clear-cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;There we have it!&lt;/p&gt;

&lt;p&gt;Adopting the Persistent Staging Flow from the Expo documentation in our CI/CD deployment pattern offers a streamlined and effective approach for managing React Native Expo app development. By consistently utilizing "staging" and "production" branches, we ensure a smooth and continuous integration and deployment process, significantly enhancing our development efficiency and release quality.&lt;/p&gt;

&lt;p&gt;Happy deploying 🚀&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
