skip to Main Content

I have svg icons These are <A/>, <B/>, <C/>.

I want to use these icons in <MyButton>.

This is how I am using it now.

MyButton.js

function MyButton(props) {
  return (
    <TouchableOpacity>
      {props.icon}
      <Text>{props.title}</Text>
    </TouchableOpacity>
  );
}

App.js

import A from "./A";
import B from "./B";  
import C from "./C";  

function App() {
      return (
        <MyButton icon={<A/>} title={'Example'} />
      );
    }

I want the icons to be selectable with string instead of sending them with props. How can I do that?

_App.js

function App() {
      return (
        <MyButton icon={"a"} title={'Example'} />
      );
    }

4

Answers


  1. You can follow this approach. Creating an object of icons, then you can pass icon as string from props to MyButton Component.

    function MyButton(props) {
      const icons = {
        "a": require("./A"),
        "b": require("./B"),
        "c": require("./C")
      }
      const IconToRender = props.icon ? icons[props.icon] : null;
      return (
        <TouchableOpacity>
          {
            IconToRender ? <IconToRender/> : null
          }
          <Text>{props.title}</Text>
        </TouchableOpacity>
      );
    }
    

    Usage

     <MyButton icon="a" title="My Title" />
    

    Let me know in comments if it helps!

    Login or Signup to reply.
  2. Solution 1

    import React from 'react';
    import {Text, TouchableOpacity} from 'react-native';
    import A from './A';
    import B from './B';
    import C from './C';
    
    const MyButton = ({icon, label}) => {
      return (
        <TouchableOpacity>
          {icon == 'a' ? <A /> : icon == 'b' ? <B /> : <C />}
          <Text>{label}</Text>
        </TouchableOpacity>
      );
    };
    
    export default MyButton;
    
    
    
    <MyButton icon={"a"} label={"SVG A"} />
    <MyButton icon={"b"} label={"SVG B"} />
    <MyButton icon={"c"} label={"SVG C"} />
    

    Solution 2

    import React from 'react';
    import {Text, TouchableOpacity} from 'react-native';
    import A from './A';
    import B from './B';
    import C from './C';
    const ICON_SIZE =20;
    const icons_array = {
        "a":<A width={ICON_SIZE} heigth={ICON_SIZE} />,
        "b":<B width={ICON_SIZE} heigth={ICON_SIZE} />,
        "c":<C width={ICON_SIZE} heigth={ICON_SIZE} />
      }
    const MyButton = ({icon, label}) => {
      return (
        <TouchableOpacity>
          {icons_array[icon]}
          <Text>{label}</Text>
        </TouchableOpacity>
      );
    };
    
    export default MyButton;
    
    
    <MyButton icon={"a"} label={"SVG A"} />
    <MyButton icon={"b"} label={"SVG B"} />
    <MyButton icon={"c"} label={"SVG C"} />
    
    Login or Signup to reply.
  3. Your SVG icons are (very probably) React components. I’m afraid there is no (clean/reliable) way to access those components by a string, because they are basically a javascript function (or a "class"), and are not accessible via a string. (except if already mapped to some object, even if it’s the window object).

    string-to-object mapping in javascript

    Accessing something "by string" in javascript means finding a string inside a collection, in some way or another, where the "collection" might be an object, an array, or even a string itself.

    There is no javascript collection of your components per se, so to be able to select one component "by string", you need to create a collection first, i.e. some list of key-value pairs that associate a string to each component, as other answers already suggested, e.g.

    const icons = {
        a: A,    // where `A` was imported or required
        b: B,
        ...
    }
    

    Furthermore, as soon as a React component is "known" to javascript, it takes up memory space.

    I.e. as soon as there is a javascript key-to-object mapping, that means that the objects exists from javascripts perspective, so if you have hundreds icons, and a mapping, then you have hundreds of components at once in the memory (while you probably only need a few at a time).

    file system mapping

    However, some libraries use specific file names, i.e. they use the file system as a "mapping" (i.e. the key is the file name, the value is the actual file data), and dynamically create an URL, e.g.

    file system:

    /icons/my_smiley-face_icon.svg
    /icons/my_home_icon.svg
    ...
    

    code:

    <MyImage name="smiley-face">
    
    const MyImage = function({ name }){
      return <img src={ '/icons/my_' + name + '_icon.svg' } />
    }
    

    The URL could be used to fetch( url ) some image data, or as a source for an <img> tag, or incorporated into the DOM in other, possibly weird ways. But they don’t use React components here.

    Build time

    Other libraries might auto-map icon components to javascript identifiers with some build-time- or pre-build-tools, e.g. some script might read the contents of one specific folder, and even analyzing your code and finding icon names in it, and import only the used components.

    Login or Signup to reply.
  4. After so many years working in react-native, here is the way I am handeling button icons:

    File structure

    .
    ├── App.tsx
    ├── Button.tsx
    ├── assets
    │   ├── index.ts
    │   └── A.tsx
    │   └── B.tsx
    │   └── C.tsx
    

    assets/A.tsx: auto generated by SVGR. Can be made manually

    import * as React from "react"
    import Svg, { SvgProps, Circle } from "react-native-svg"
    
    const A = (props: SvgProps) => (
      <Svg height={24} width={24} {...props}>
        <Circle cx={50} cy={50} r={40} stroke="black" strokeWidth={3} fill="red" />
      </Svg>
    )
    
    export default A
    

    assets/index.ts: auto generated by SVGR. Can be made manually

    export { default as A } from "./A";
    export { default as B } from "./B";
    export { default as C } from "./C";
    

    Button.tsx

    // other imports
    import * as Icons from "./assets";
    
    interface IIconProps {
      height?: string | number;
      width?: string | number;
      name?: keyof typeof Icons | null; // enable vscode sugestion as well
    };
    
    function RenderIcon({ name, ...props }: IIconProps) {
      if (!name) return null;
      const Icon = Icons[name];
      return <Icon {...props} />;
    };
    
    interface IButtonProps {
      title?: string;
      icon?: IIconProps;
    };
    
    function Button({ icon, title, icon }: IButtonProps) {
      return (
        <TouchableOpacity>
          <RenderIcon {...icon} />
          <Text>{title || ""}</Text>
        </TouchableOpacity>
      );
    };
    
    export default Button;
    

    App.tsx

    function App() {
      return <Button icon={{ name: "A", width: 10, height: 10 }} title="Example" />
    }
    

    Hope it will help.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search