web-dev-qa-db-fra.com

React / Nom du composant dynamique JSX

J'essaie de rendre dynamiquement les composants en fonction de leur type.

Par exemple:

var type = "Example";
var ComponentName = type + "Component";
return <ComponentName />; 
// Returns <examplecomponent />  instead of <ExampleComponent />

J'ai essayé la solution proposée ici noms des composants dynamiques React/JSX

Cela m'a donné une erreur lors de la compilation (en utilisant browserify pour gulp). Il s’attendait à ce que XML utilise une syntaxe de tableau.

Je pourrais résoudre ce problème en créant une méthode pour chaque composant:

newExampleComponent() {
    return <ExampleComponent />;
}

newComponent(type) {
    return this["new" + type + "Component"]();
}

Mais cela signifierait une nouvelle méthode pour chaque composant que je crée. Il doit y avoir une solution plus élégante à ce problème.

Je suis très ouvert aux suggestions.

129
Sam

<MyComponent /> est compilé en React.createElement(MyComponent, {}), qui attend une chaîne (balise HTML) ou une fonction (ReactClass) comme premier paramètre.

Vous pouvez simplement stocker votre classe de composants dans une variable dont le nom commence par une lettre majuscule. Voir balises HTML vs React Components .

var MyComponent = Components[type + "Component"];
return <MyComponent />;

compile pour

var MyComponent = Components[type + "Component"];
return React.createElement(MyComponent, {});
128

Une documentation officielle sur la façon de gérer de telles situations est disponible ici: https://facebook.github.io/react/docs/jsx-in-depth.html#choosing-the-type-at-runtime

En gros, il est écrit:

Faux:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
    photo: PhotoStory,
    video: VideoStory
};

function Story(props) {
    // Wrong! JSX type can't be an expression.
    return <components[props.storyType] story={props.story} />;
}

Correct:

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
    photo: PhotoStory,
    video: VideoStory
};

function Story(props) {
    // Correct! JSX type can be a capitalized variable.
    const SpecificStory = components[props.storyType];
    return <SpecificStory story={props.story} />;
}
107
gmfvpereira

J'ai trouvé une nouvelle solution. Notez que j'utilise des modules ES6 et que j'ai donc besoin du cours. Vous pouvez également définir une nouvelle classe React.

var components = {
    example: React.createFactory( require('./ExampleComponent') )
};

var type = "example";

newComponent() {
    return components[type]({ attribute: "value" });
}
10
Sam

Si vos composants sont globaux, vous pouvez simplement faire:

var nameOfComponent = "SomeComponent";
React.createElement(window[nameOfComponent], {});
6
Ray

Pour un composant wrapper, une solution simple consisterait simplement à utiliser React.createElement directement (à l'aide de ES6).

import RaisedButton from 'mui/RaisedButton'
import FlatButton from 'mui/FlatButton'
import IconButton from 'mui/IconButton'

class Button extends React.Component {
  render() {
    const { type, ...props } = this.props

    let button = null
    switch (type) {
      case 'flat': button = FlatButton
      break
      case 'icon': button = IconButton
      break
      default: button = RaisedButton
      break
    }

    return (
      React.createElement(button, { ...props, disableTouchRipple: true, disableFocusRipple: true })
    )
  }
}
6
Ricardo Pedroni

Un conteneur doit mapper les noms de composants sur tous les composants censés être utilisés de manière dynamique. Les classes de composants doivent être enregistrées dans un conteneur, car dans un environnement modulaire, il n’existe pas d’autre endroit unique pour y accéder. Les classes de composants ne peuvent pas être identifiées par leurs noms sans les spécifier explicitement car la fonction name est réduite en production.

Carte de composants

Ce peut être un objet simple:

class Foo extends React.Component { ... }
...
const componentsMap = { Foo, Bar };
...
const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;

Ou Map instance:

const componentsMap = new Map([[Foo, Foo], [Bar, Bar]]);
...
const DynamicComponent = componentsMap.get(componentName);

L'objet brut est plus approprié car il bénéficie d'un raccourci de propriété.

Module de baril

Un module baril avec les exportations nommées peut agir comme tel:

// Foo.js
export class Foo extends React.Component { ... }

// dynamic-components.js
export * from './Foo';
export * from './Bar';

// some module that uses dynamic component
import * as componentsMap from './dynamic-components';

const componentName = 'Fo' + 'o';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;

Cela fonctionne bien avec une classe par style de code de module.

Décorateur

Les décorateurs peuvent être utilisés avec des composants de classe pour le sucre syntaxique. Cela nécessite néanmoins de spécifier explicitement les noms de classe et de les enregistrer dans une carte:

const componentsMap = {};

function dynamic(Component) {
  if (!Component.displayName)
    throw new Error('no name');

  componentsMap[Component.displayName] = Component;

  return Component;
}

...

@dynamic
class Foo extends React.Component {
  static displayName = 'Foo'
  ...
}

Un décorateur peut être utilisé comme composant d'ordre supérieur avec des composants fonctionnels:

const Bar = props => ...;
Bar.displayName = 'Bar';

export default dynamic(Bar);

L'utilisation de non standard displayName au lieu de la propriété aléatoire est également bénéfique pour le débogage.

4
Estus Flask

Nous souhaitons accéder à différentes vues lors du chargement dynamique des composants. Le code suivant donne un exemple de travail sur la façon de procéder en utilisant une chaîne analysée à partir de la chaîne de recherche d'une URL.

Supposons que nous souhaitons accéder à une page 'snozberrys' avec deux vues uniques utilisant ces chemins d'URL:

'http://localhost:3000/snozberrys?aComponent'

et

'http://localhost:3000/snozberrys?bComponent'

nous définissons le contrôleur de notre vue comme ceci:

import React, { Component } from 'react';
import ReactDOM from 'react-dom'
import {
  BrowserRouter as Router,
  Route
} from 'react-router-dom'
import AComponent from './AComponent.js';
import CoBComponent sole from './BComponent.js';

const views = {
  aComponent: <AComponent />,
  console: <BComponent />
}

const View = (props) => {
  let name = props.location.search.substr(1);
  let view = views[name];
  if(view == null) throw "View '" + name + "' is undefined";
  return view;
}

class ViewManager extends Component {
  render() {
    return (
      <Router>
        <div>
          <Route path='/' component={View}/>
        </div>
      </Router>
    );
  }
}

export default ViewManager

ReactDOM.render(<ViewManager />, document.getElementById('root'));
0
1-14x0r

J'ai utilisé une approche un peu différente, car nous connaissons toujours nos composants réels et j'ai donc pensé à appliquer le boîtier d'interrupteur. En outre, le nombre total de composants était d'environ 7-8 dans mon cas.

getSubComponent(name) {
    let customProps = {
       "prop1" :"",
       "prop2":"",
       "prop3":"",
       "prop4":""
    }

    switch (name) {
      case "Component1": return <Component1 {...this.props} {...customProps} />
      case "Component2": return <Component2 {...this.props} {...customProps} />
      case "component3": return <component3 {...this.props} {...customProps} />

    }
  }
0
abhirathore2006