web-dev-qa-db-fra.com

Comment importer un dossier entier d'images SVG (ou comment les charger dynamiquement) dans une React Web App?

J'ai un composant qui prend un: itemName et crache un bundle html contenant une image. L'image est différente pour chaque bundle.

Voici ce que j'ai:

import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';

import SVGInline from "react-svg-inline";

export default (props) => (
  <NavLink className="hex" activeClassName="active" to={'/hex/' + props.itemName}> {React.createElement(SVGInline, {svg: props.itemName})} </NavLink>
)

Comment pourrais-je faire fonctionner ce composant?

Je sais que si je viens d'importer toutes mes images explicitement, je pourrais simplement appeler mes images comme ça ...

import SVGInline from "react-svg-inline";
import SASSSVG from "./images/sass.svg";

<NavLink className="hex" activeClassName="active" to="/hex/sass"><SVGInline svg={ SASSSVG } /></NavLink>

Cela fonctionnerait, mais comme je dois inclure ~ 60 svgs, cela ajoute BEAUCOUP de code en excès.

Aussi, j'ai trouvé dans cette question ce code ...

import * as IconID from './icons';

Mais cela ne semble pas fonctionner (cela faisait partie de la question, pas de la réponse), et la réponse était un peu trop spécifique pour répondre à la question que je pose.

J'ai également trouvé cette question mais encore une fois il y a une réponse (bien que non approuvée) qui possède plus de questions qu'elle n'en répond. Donc, après avoir installé react-svg, j'ai mis en place un test pour voir si la réponse fonctionne comme ça ...

import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import { NavLink } from 'react-router-dom';
import ReactSVG from 'react-svg'

export default (props) => (
  <NavLink className="hex" activeClassName="active" to={'/hex/' + props.itemName}>
    <ReactSVG
      path={"images/" + props.itemName + ".svg"}
      callback={svg => console.log(svg)}
      className="example"
    />
  </NavLink>
)

Mais, tout comme l'OP de cette question le demandait, la page ne peut pas trouver mon svg même après avoir copié tout mon dossier d'image dans mon dossier de construction. J'ai aussi essayé "./images/"

J'ai l'impression qu'il me manque juste une dernière information clé et après avoir recherché le jour dernier, j'espérais que quelqu'un pourrait identifier la pièce qui me manque.

15
WiseOlMan

Si vous utilisez React, je soupçonne fortement que vous utilisez également Webpack. Vous pouvez utiliser require.context au lieu de es6 import et Webpack le résoudra pour vous lors de la construction.

require.context ( folder, recurse, pattern )
  • folder - String - Chemin d'accès au dossier pour commencer la recherche de fichiers.
  • recurse - Boolean - Indique s'il faut analyser récursivement le dossier.
  • pattern - RegExp - Modèle de correspondance décrivant les fichiers à inclure.

La première ligne de chaque exemple ...

const reqSvgs = require.context ( './images', true, /\.svg$/ )

... crée un mappage de contexte requis tous les *.svg chemins de fichier dans le dossier images vers une importation. Cela nous donne une fonction requise spécialisée nommée reqSvgs avec quelques propriétés attachées.

L'une des propriétés de reqSvgs est une méthode keys, qui renvoie une liste de tous les chemins de fichiers valides.

const allSvgFilepaths = reqSvgs.keys ()

Nous pouvons passer l'un de ces chemins de fichiers dans reqSvgs pour obtenir une image importée.

const imagePath = allSvgFilePaths[0]
const image = reqSvgs ( imagePath )

Cette API est contraignante et peu intuitive pour ce cas d'utilisation, je suggère donc de convertir la collection en une structure de données JavaScript plus courante pour en faciliter l'utilisation.

Chaque image sera importée lors de la conversion. Faites attention, car cela pourrait être une arme à feu. Mais il fournit un mécanisme raisonnablement simple pour copier plusieurs fichiers dans le dossier de construction qui pourraient ne jamais être explicitement référencés par le reste de votre code source.

Voici 3 exemples de conversions qui pourraient être utiles.


Array

Créez un tableau des fichiers importés.

const reqSvgs = require.context ( './images', true, /\.svg$/ )
const paths = reqSvgs.keys ()

const svgs = paths.map( path => reqSvgs ( path ) )

Tableau d'objets

Créez un tableau d'objets, chaque objet étant { path, file } pour une image.

const reqSvgs = require.context ( './images', true, /\.svg$/ )

const svgs = reqSvgs
  .keys ()
  .map ( path => { path, file: svg ( path ) } )

Objet simple

Créez un objet où chaque chemin est une clé de son fichier correspondant.

const reqSvgs = require.context ('./images', true, /\.svg$/ )

const svgs = reqSvgs
  .keys ()
  .reduce ( ( images, path ) => {
    images[path] = reqSvgs ( path )
    return images
  }, {} )

SurviveJS donne un exemple plus généralisé de require.context ici Chargement dynamique de SurviveJS Webpack .

11
skylize

Au lieu de plusieurs fichiers SVG, vous pouvez utiliser le sprite SVG unique.

SVG Sprite peut être généré à partir d'un répertoire de fichiers SVG en utilisant svg-Sprite-generator :

svg-Sprite-generate -d images -o images/Sprite.svg

Ensuite, utilisez-le comme ceci:

import React from 'react';
import { NavLink } from 'react-router-dom';
import Sprite from './images/Sprite.svg';

export default (props) => (
  <NavLink className="hex" activeClassName="active" to={'/hex/' + props.itemName}>
    <svg xmlns="http://www.w3.org/2000/svg" width="30" height="30">
      <use xlinkHref={`${Sprite}#${props.itemName}`} />
    </svg>
  </NavLink>
)
2
quotesBro

La meilleure façon est d'utiliser un module de nœud comme [SVG pour React Loader] ( https://github.com/jhamlet/svg-react-loader )

1
karthik

Vous pouvez simplement créer une fonction qui prend un paramètre "nom" (votre nom d'icône svg) et renvoyer votre code svg.

import React from 'react' export function getIcon(name){ switch(name) { case 'back': return ( // your svg code here <svg></svg> ) } }

Et puis vous pouvez l'importer n'importe où et l'appeler simplement avec le nom d'icône que vous voulez.

import { getIcon } from './utils'
render() {
  return (
    <div>
     <span>{ getIcon('back') }</span>
    </div>
  )
}
0
madhurgarg

Je suis tombé sur ce problème - j'avais initialement la "réponse acceptée", mais j'ai provoqué une demande http pour chaque svg, ce qui a déclenché une limite de débit. J'ai donc fini par combiner la réponse acceptée et ce que @karthik a proposé - en utilisant un chargeur dans le request.context

À partir de CRA 2.0, @svgr est inclus pour importer des svg en tant que composants react.

const reqSvgs = require.context('!@svgr/webpack!flag-icon-css/flags/4x3', true, /\.svg$/)

Nous combinons donc ici un chargeur svg et require.context

const flagMap = reqSvgs.keys().reduce((images, path) => {
  const key = path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf('.'))
  images[key] = reqSvgs(path).default
  return images
}, {})

Ensuite, nous mappons tout cela dans un objet json afin que nous puissions utiliser la recherche de clé

Pour rendre des svg en jsx:

const Flag = flagMap['dk']
return (
  <Flag />
)

Et jours heureux, svgs inclus dans le bundle et aucune demande http individuelle ????????

0
Christian