Название динамического компонента React/JSX

Я пытаюсь динамически отображать компоненты на основе их типа.

Например:

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

Я попробовал предлагаемое здесь решение Динамические имена компонентов React/JSX

Это дало мне ошибку при компиляции (используя браузер для gulp). Он ожидал XML, где я использовал синтаксис массива.

Я мог бы решить это, создав метод для каждого компонента:

newExampleComponent() {
    return <ExampleComponent />;
}

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

Но это будет означать новый метод для каждого компонента, который я создаю. Должно быть более элегантное решение этой проблемы.

Я очень открыт для предложений.

Ответ 1

<MyComponent /> компилируется в React.createElement(MyComponent, {}), который ожидает, что в качестве первого параметра будет строка (HTML-тег) или функция (ReactClass).

Вы можете просто сохранить свой класс компонента в переменной с именем, которое начинается с прописной буквы. См. HTML-теги vs React Components.

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

компилируется в

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

Ответ 2

Существует официальная документация о том, как обращаться с такими ситуациями, доступна здесь: https://facebook.github.io/react/docs/jsx-in-depth.html#choosing-the-type-at-runtime

В основном это говорит:

Неправильно:

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} />;
}

Правильно:

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} />;
}

Ответ 3

Я понял новое решение. Обратите внимание, что я использую модули ES6, поэтому мне нужен класс. Вы также можете определить новый класс React.

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

var type = "example";

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

Ответ 4

Если ваши компоненты глобальны, вы можете просто сделать:

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

Ответ 5

Для компонента-оболочки простым решением будет просто использовать React.createElement напрямую (используя 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

Должен быть контейнер, который отображает имена компонентов на все компоненты, которые должны использоваться динамически. Классы компонентов должны быть зарегистрированы в контейнере, потому что в модульной среде нет единого места, где они могли бы быть доступны. Классы компонентов не могут быть идентифицированы по их именам без их явного указания, поскольку name функции минимизируется при производстве.

Карта компонентов

Это может быть простой объект:

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

Или экземпляр Map:

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

Простой объект является более подходящим, потому что он извлекает выгоду из сокращения свойств.

Ствольный модуль

Модуль ствола с именованным экспортом может действовать как такая карта:

// 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/>;

Это хорошо работает с одним классом на стиль кода модуля.

декоратор

Декораторы могут использоваться с компонентами класса для синтаксического сахара, для этого все же необходимо явно указать имена классов и зарегистрировать их в карте:

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'
  ...
}

Декоратор может использоваться как компонент более высокого порядка с функциональными компонентами:

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

export default dynamic(Bar);

Использование нестандартного displayName вместо случайного свойства также способствует отладке.

Ответ 7

Я использовал немного другой подход, так как мы всегда знаем наши фактические компоненты, поэтому я решил применить корпус коммутатора. Кроме того, общее количество компонентов не было около 7-8 в моем случае.

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} />

    }
  }

Ответ 8

Предположим, что мы хотим получить доступ к различным представлениям при загрузке динамических компонентов. Следующий код дает рабочий пример того, как это сделать, используя строку, проанализированную из строки поиска URL.

Предположим, мы хотим получить доступ к странице 'snozberrys' с двумя уникальными представлениями, используя эти URL-адреса:

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

и

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

мы определяем наш контроллер представлений следующим образом:

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'));

Ответ 9

Пожалуйста, как я могу сделать эту структуру в React native?

компонент должен быть из переменной

Ответ 10

Пожалуйста, как я могу сделать эту структуру в React native?

компонент должен быть из переменной

<Component>
<Image />

</Component>

Ответ 11

Изменить: Другие ответы лучше, см. комментарии.

Я решил ту же проблему следующим образом:

...
render : function () {
  var componentToRender = 'component1Name';
  var componentLookup = {
    component1Name : (<Component1 />),
    component2Name : (<Component2 />),
    ...
  };
  return (<div>
    {componentLookup[componentToRender]}
  </div>);
}
...