React Native: require() with Dynamic String?

As i have heard of, react's require() only uses static url not variables, that means that you have to do require('/path/file'), take a look at this issue on github and this one for more alternative solutions, there are a couple of other ways to do it! for e.g

const images = {
profile: {
    profile: require('./profile/profile.png'),
    comments: require('./profile/comments.png'),
},
   image1: require('./image1.jpg'),
   image2: require('./image2.jpg'),
};

export default images;

then

import Images from './img/index';

render() {
    <Image source={Images.profile.comments} />
}

from this answer


Here is my solution.

Setup

File structure:

app  
  |--src
    |--assets
      |--images
        |--logos
          |--small_kl_logo.png
          |--small_a1_logo.png
          |--small_kc_logo.png
          |--small_nv_logo.png
          |--small_other_logo.png

        |--index.js
    |--SearchableList.js

In index.js, I have this:

const images = {
  logos: {
    kl: require('./logos/small_kl_logo.png'),
    a1: require('./logos/small_a1_logo.png'),
    kc: require('./logos/small_kc_logo.png'),
    nv: require('./logos/small_nv_logo.png'),
    other: require('./logos/small_other_logo.png'),
  }
};

export default images;

In my SearchableList.js component, I then imported the Images component like this:

import Images from './assets/images';

I then created a new function imageSelect in my component:

imageSelect = network => {
  if (network === null) {
    return Images.logos.other;
  }

  const networkArray = {
    'KL': Images.logos.kl,
    'A1': Images.logos.a1,
    'KC': Images.logos.kc,
    'NV': Images.logos.nv,
    'Other': Images.logos.other,
  };

  return networkArray[network];
};

Then in my components render function I call this new imageSelect function to dynamically assign the desired Image based on the value in the this.state.network:

render() {
  <Image source={this.imageSelect(this.state.network)} />
}

The value passed into the imageSelect function could be any dynamic string. I just chose to have it set in the state first and then passed in.

I hope this answer helps. :)


I have found that a dynamic path for require() works when it starts with a static string. For example require("./" + path) works, whereas require(path) doesn't.


For anyone reading this that cannot work with the existing answers, I have an alternative.

First I'll explain my scenario. We have a mono repo with a number of packages (large react-native app). I want to dynamically import a bunch of locale files for i18n without having to keep a central registry in some magic file. There could be a number of teams working in the same monorepo and the DX we want is for package developers to be able to just add their local files in a known folder {{packageName}}/locales/en.json and have our core i18n functionality pick up their strings.

After several less than ideal solutions, I finally landed on https://github.com/kentcdodds/babel-plugin-preval as an ideal solution for us. This is how I did it:

const packageEnFiles = preval`
  const fs = require('fs');
  const path = require('path');

  const paths = [];

  const pathToPackages = path.join(__dirname, '../../../../packages/');
fs.readdirSync(pathToPackages)
    .filter(name => fs.lstatSync(path.join(pathToPackages, name)).isDirectory())
    .forEach(dir => {
      if (fs.readdirSync(path.join(pathToPackages, dir)).find(name => name === 'locales')) {
        const rawContents = fs.readFileSync(path.join(pathToPackages, dir, 'locales/en.json'), 'utf8');
        paths.push({
          name: dir,
          contents: JSON.parse(rawContents),
        });
      }
    });

  module.exports = paths;
`;

Then I can just iterate over this list and add the local files to i18next:

packageEnFiles.forEach(file => {
  i18n.addResourceBundle('en', file.name, file.contents);
});

If you need to switch between multiple locally stored images, you can also use this way:

        var titleImg;
        var textColor;
        switch (this.props.data.title) {
        case 'Футбол':
            titleImg = require('../res/soccer.png');
            textColor = '#76a963';
            break;
        case 'Баскетбол':
            titleImg = require('../res/basketball.png');
            textColor = '#d47b19';
            break;
        case 'Хоккей':
            titleImg = require('../res/hockey.png');
            textColor = '#3381d0';
            break;
        case 'Теннис':
            titleImg = require('../res/tennis.png');
            textColor = '#d6b031';
            break;
        }

In this snippet I change variables titleImg and textColor depending of the prop. I have put this snippet directly in render() method.