web-dev-qa-db-fra.com

Créer une instance d'une classe React à partir d'une chaîne

J'ai une chaîne qui contient un nom de la classe (cela provient d'un fichier json). Cette chaîne indique à ma classe de modèles la disposition/modèle à utiliser pour les données (également en json). Le problème est que ma mise en page ne s'affiche pas.

Home.jsx:

//a template or layout.
var Home = React.createClass({
  render () {
    return (
    <div>Home layout</div>
    )
  }
});

Template.jsx:

var Template = React.createClass({
  render: function() {
    var Tag = this.props.template; //this is the name of the class eg. 'Home'
    return (
        <Tag />
    );
  }
});

Je ne reçois aucune erreur, mais je ne vois pas non plus la mise en page/Home Class. J'ai vérifié le props.template et cela enregistre les informations correctes. De plus, je peux voir l'élément home dans le DOM. Cependant cela ressemble à ceci:

<div id='template-holder>
    <home></home>
</div>

Si je change de ligne suivante en:

var Tag = Home;
//this works but it's not dynamic!

Des idées, comment je peux résoudre ce problème? Je suis sûr que c'est une solution simple ou que je fais quelque chose de stupide. L'aide serait appréciée. Excuses si cela a déjà été demandé (je ne pouvais pas le trouver).

Merci, Ewan

16
ewan

Cela ne fonctionnera pas:

var Home = React.createClass({ ... });

var Component = "Home";
React.render(<Component />, ...);

Cependant, cela:

var Home = React.createClass({ ... });

var Component = Home;
React.render(<Component />, ...);

Vous devez donc simplement trouver un moyen de mapper entre string"Home" et la classe de composants Home. Un objet simple fonctionnera comme un registre de base et vous pourrez le construire si vous avez besoin de plus de fonctionnalités.

var components = {
  "Home": Home,
  "Other": OtherComponent
};

var Component = components[this.props.template];
13
Michelle Tilley

Si vous pouvez vivre avec tous vos composants dans un module, cela fonctionne sans avoir à mapper manuellement vos classes dans un dictionnaire:

   import * as widgets from 'widgets';
   var Type = widgets[this.props.template];
   ...
   <Type />

L'instruction d'importation avec caractère générique est déjà un dictionnaire et le code fonctionne comme le registre de la réponse précédente.

En fait, je pense que vous pourriez travailler avec plusieurs modules avec un mappage supplémentaire:

import * as widgets from 'widgets';
import * as widgets2 from 'widgets2';

const registry = Object.assign({}, widgets, widgets2);
const widget = registry[this.props.template];

Je voudrais totalement faire ça. En fait je pense que je suis.

6
Robert Moskal

J'ai eu le même problème et j'ai trouvé la solution par moi-même. Je ne sais pas si c'est la "meilleure pratique" mais cela fonctionne et je l'utilise actuellement dans ma solution.

Vous pouvez simplement utiliser la fonction "evil" eval pour créer dynamiquement une instance d'un composant react. Quelque chose comme:

function createComponent(componentName, props, children){
  var component = React.createElement(eval(componentName), props, children);
  return component;
}

Ensuite, appelez-le où vous voulez:

var homeComponent = createComponent('Home', [props], [...children]);

Si cela répond à vos besoins, vous pouvez peut-être envisager une solution de ce type.

J'espère que ça aide.

2
dgpedro

Lorsque vous utilisez JSX, vous pouvez rendre les balises HTML (chaînes) ou les composants React (classes).

Lorsque vous faites var Tag = Home, cela fonctionne car le compilateur JSX le transforme en:

var Template = React.createElement(Tag, {});

avec la variable Tag dans la même portée et étant une classe React.

    var Tag = Home = React.createClass({
                       render () {
                         return (
                         <div>Home layout</div>
                         )
                       }
                     });

Quand tu fais 

var Tag = this.props.template; // example: Tag = "aClassName"

tu es en train de faire

var Template = React.createElement("aClassName", null);

Mais "aClassName" n'est pas une balise HTML valide.

Regardez ici

1
gabrielgiussi

Je voulais savoir comment créer des classes React de manière dynamique à partir d'une spécification JSON chargée à partir d'une base de données. J'ai donc expérimenté et compris le résultat. Mon idée de base était que je souhaitais définir une application React via une interface graphique plutôt que de saisir du code dans un éditeur de texte.

Ceci est compatible avec React 16.3.2. Note React.createClass a été déplacé dans son propre module.

Voici la version condensée des pièces essentielles:

import React from 'react'
import ReactDOMServer from 'react-dom/server'
import createReactClass from 'create-react-class'

const spec = {
  // getDefaultProps
  // getInitialState
  // propTypes: { ... }
  render () {
    return React.createElement('div', null, 'Some text to render')
  }
}
const component = createReactClass(spec)
const factory = React.createFactory(component)
const instance = factory({ /* props */ })
const str = ReactDOMServer.renderToStaticMarkup(instance)
console.log(str)

Vous pouvez voir un exemple plus complet ici:

https://github.com/brennancheung/02-dynamic-react/blob/master/src/commands/tests/createClass.test.js

0
Brennan Cheung