upleveled / expo-example-spring-2024-atvie

2 stars 0 forks source link

Integrate displaying dynamic images in lecture? #1

Open ProchaLu opened 4 months ago

ProchaLu commented 4 months ago

Takeover from https://github.com/hola-soy-milk/upleveled-react-native-expo/issues/14

Many students struggle to dynamically display images in React Native, and often attempt to use require along with props to dynamically source images. However, this is incompatible with dynamic paths because Expo pre-bundles all assets at build time, making dynamic loading at runtime with require impossible, and using require only works with static images. This results in the following error:

<Image source={require{`../assets/upleveled-${id}.jpeg`}} />
error: app/user/[id].tsx: app/user/[id].tsx:Invalid call at line 27: require(`../../assets/upleveled-${id}.jpeg`)
 ERROR  [Error: TransformError app/user/[id].tsx: app/user/[id].tsx:Invalid call at line 27: require(`../../assets/upleveled-${id}.jpeg`)]
Error: app/user/[id].tsx:Invalid call at line 27: require(`../../assets/upleveled-${id}.jpeg`)
    at transformJS (/Users/lukas/Documents/projects/upleveled-react-native-expo/register-login/node_modules/@expo/metro-config/build/transform-worker/metro-transform-worker.js:228:23)
    at transformJSWithBabel (/Users/lukas/Documents/projects/upleveled-react-native-expo/register-login/node_modules/@expo/metro-config/build/transform-worker/metro-transform-worker.js:316:18)
Screenshot 2024-02-29 at 11 03 33 Screenshot 2024-03-01 at 11 37 24

Solution

To address this issue, we should teach students how to dynamically display images in React Native using best practices.

The recommended solution for me is to use direct URLs to the images and use the image URL in the source attribute. In React Native, the source attribute expects an object containing the uri attribute rather than a string. By following this method, students can dynamically use images and display them within the app using the uri attribute.

<Image source={{uri: props.imageUrl}} />

This approach is simple and effective and helps students overcome the challenges of dynamically displaying images in their React Native apps. Key benefits include:

As an image host, I would recommend to use Cloudinary. There are also other image hosting services, but Cloudinary is very popular and offers a free tier that students can use to host their images.

Alternative Solutions

Using require.context

Using require.context to dynamically import images based on a regular expression pattern matching image filenames. This usage of require.context will throw a TS error Property 'context' does not exist on type 'NodeRequire'., for this issue we can add @types/webpack-env package to resolve this error.

Pros:

Cons:

  const images = require.context('../../assets', true, /\.jpeg$/);

  const imageSources = images.keys().map((key) => images(key));

      <Image
        style={{
          width: 200,
          height: 200,
        }}
        source={imageSources[Number(id) - 1]}
      />

Express Static File Serving

Express Static File Serving, which involves setting up a server to serve static files. However, this is more complex and requires more setup, also using Express, and not the Expo API Routes

Pros:

Cons:

File with require image paths

This involves creating an assets/index.js file where image paths are defined as variables, or defined as an object. Students can then import this variable and use the imported variable for the require statement. While this approach works, it's considered less optimal compared to using direct URLs.

Pros:

Cons:

assets/index.js

export const product1 = require('./product-1.jpeg');
export const product2 = require('./product-2.jpeg');

app/index.js

import { Image } from 'react-native';
import * as All from '../assets/index.js';

type Props = {
  imageUrl: string;
};

export default function ImageComp(props: Props) {
  return (
    <Image
      style={{
        width: 200,
        height: 200,
      }}
      source={All[props.imageUrl]}
    />
  );
}

Import function

Function to import all images from a directory and use the imported images object to dynamically select the image based on the id.

Pros:

Cons:

function importAll(r) {
  let images = {};
  r.keys().forEach((item) => {
    images[item.replace('./', '')] = r(item);
  });
  return images;
}

// Import all images from the images directory
const images = importAll(
  require.context('../../assets', false, /\.(png|jpe?g|svg)$/),
);

source={images[`upleveled-${id}.jpeg`]}