2015-04-26 3 views
81

Я пытаюсь динамически визуализировать компоненты на основе их типа.React/JSX Динамическое имя компонента

Например:

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

Я попробовал решение, предложенное здесь React/JSX dynamic component names

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

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

newExampleComponent() { 
    return <ExampleComponent />; 
} 

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

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

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

ответ

96

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

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

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

компилирует

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

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

+1

Будущие читатели также наверняка найдут '{... this.props}' полезным для прозрачного прохождения реквизита к подтипированным компонентам из родителя. Как 'return ' –

+3

Также убедитесь, что вы используете имя своей динамической переменной. – saada

9

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

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

var type = "example"; 

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

Итак, это отлично работает для меня, когда я использую 'module.exports = MyComponent', но я получаю сообщение об ошибке, когда я использую' expo rt default MyComponent'. Кто-нибудь еще сталкивается с этим? – klinore

+0

@klinore Вы пытались получить доступ к атрибуту 'default'? i.e: require ('./ ExampleComponent'). default? –

5

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

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

+0

Это прекрасно работает, особенно если используется Rails. Принятый ответ не работает для меня, потому что массив 'Components' не определен. – Vadim

+0

Красивая! лол ! – bertie

+0

Вместо того, чтобы прикреплять произвольно именованные объекты к глобальной области (euw), вам следует рассмотреть возможность сохранения реестра компонентов, который вы можете зарегистрировать, а затем получить ссылки на компоненты, когда это необходимо. – slashwhatever

-1

Я использовал немного другой подход, как мы всегда знаем, что наши фактические компоненты поэтому я решил применить корпус переключателя. Также общее количество компонентов было около 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} /> 

    } 
    } 
+0

Только что наткнулся на это снова. Это способ сделать это. В любом случае, вы всегда знаете свои компоненты и должны их загрузить. Так что это отличное решение. Благодарю. – Jake

+0

Означает ли это всерьез это много голосов? –

0

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

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

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

Вы, вероятно, не хотите этого делать, потому что 'React.createElement' будет вызываться для каждого компонента вашего объекта поиска, хотя только один из них будет отображаться за раз. Хуже того, поместив объект lookup в метод 'render' родительского компонента, он будет делать это снова каждый раз, когда родительский объект будет визуализирован. Лучшие ответы - это гораздо лучший способ достичь того же. – Inkling

+1

@Инклинг, я согласен. Это было, когда я только начинал с Реакта. Я написал это, а потом забыл об этом, когда узнал лучше. Спасибо что подметил это. –

1

Для компонента обертки, простое решение было бы просто использовать 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 }) 
    ) 
    } 
} 
+0

У меня на самом деле есть компонент с той же целью в моем проекте) – Dziamid

59

Существует официальная документация о том, как обращаться с такими ситуациями доступна здесь: 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} />; 
} 
+7

ОЧЕНЬ ВАЖНО: a ** капитализированная ** переменная – mpyw

0

Предположим, что мы хотим получить доступ к различным представлениям с динамической загрузкой компонентов. Следующий код дает рабочий пример того, как это сделать, используя строку, проанализированную из строки поиска 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')); 
Смежные вопросы