React / JSX Dinamik Bileşen Adı


168

Dinamik olarak türlerine göre bileşenleri işlemeye çalışıyorum.

Örneğin:

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

Burada önerilen çözümü denedim React / JSX dinamik bileşen adları

Bu derlerken bana bir hata verdi (yudum için browserify kullanarak). Bir dizi sözdizimi kullandığım XML bekleniyordu.

Bunu her bileşen için bir yöntem oluşturarak çözebilirim:

newExampleComponent() {
    return <ExampleComponent />;
}

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

Ancak bu, oluşturduğum her bileşen için yeni bir yöntem anlamına gelir. Bu soruna daha zarif bir çözüm olmalı.

Önerilere çok açığım.

Yanıtlar:


158

<MyComponent />derlenir React.createElement(MyComponent, {}), ilk parametre olarak bir dize (HTML etiketi) veya bir işlev (ReactClass) bekler.

Bileşen sınıfınızı, büyük harfle başlayan bir ada sahip bir değişkende saklayabilirsiniz. Bkz. HTML etiketleri ve React Bileşenleri .

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

derlemek

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

5
Gelecekteki okuyucular da {...this.props}sahne malzemelerini alt öğelerin bileşenlerinden şeffaf bir şekilde geçirmeleri için muhtemelen yararlı bulacaktır . Likereturn <MyComponent {...this.props} />
Dr.Strangelove

4
Ayrıca dinamik değişken adınızı büyük harfle kullandığınızdan emin olun.
saada

28
Değişkeninizin yalnızca bileşenin adını dize olarak değil , bileşenin kendisini de tutması gerektiğini unutmayın .
totymedli

3
Varlığın neden büyük harfle yazılması gerektiğini de merak ediyorsanız facebook.github.io/react/docs/…
Nobita

3
Components undefined
ness-EE

144

Bu tür durumların nasıl ele alınacağına dair resmi bir belge bulunmaktadır: https://facebook.github.io/react/docs/jsx-in-depth.html#choosing-the-type-at-runtime

Temelde şöyle diyor:

Yanlış:

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

Doğru:

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

25
ÇOK ÖNEMLİ ŞEY: büyük
harfli

4
Resmi dokümanlar olduğu gerçeğinin yanı sıra, kolayca en iyi cevap ve en yapılandırılmış çözümdür. Belki de bu yüzden dokümanlar
:)

1
Harika cevap için teşekkürler. Aşağıdaki okuyucular için, harita nesnesindeki değerin (buradaki harita nesnesi const bileşenleri olduğunu ve değerin PhotoStory ve VideoStory olduğunu) tırnak işaretleri olmadan gerektiğini unutmayın - Aksi takdirde, bileşen oluşturulmayacak ve hata alacaksınız - davayı kaçırdım ve zamanı boşa harcadım ...
Erez Lieberman

11

Bileşen adlarını, dinamik olarak kullanılması gereken tüm bileşenlerle eşleyen bir kap olmalıdır. Bileşen sınıfları bir kapta kaydedilmelidir, çünkü modüler ortamda erişilebilecekleri tek bir yer yoktur. Bileşen sınıfları, işlev nameüretimde küçültüldüğünden, açıkça belirtilmeden adlarıyla tanımlanamaz .

Bileşen haritası

Düz nesne olabilir:

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

Veya Mapörnek:

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

Düz obje daha uygundur çünkü mülkten steno faydalanır.

Namlu modülü

Belirtilen ihracatlara sahip bir varil modülü bu harita gibi davranabilir:

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

Bu, modül kodu stili başına bir sınıfla iyi çalışır.

Dekoratör

Dekoratörler, sözdizimsel şeker için sınıf bileşenleriyle birlikte kullanılabilir, bunun için yine de sınıf adlarının açıkça belirtilmesi ve bir haritaya kaydedilmesi gerekir:

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

Bir dekoratör, işlevsel bileşenlere sahip daha üst düzey bileşen olarak kullanılabilir:

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

export default dynamic(Bar);

Rasgele özellik yerine standart dışıdisplayName kullanılması da hata ayıklamaya yarar.


Teşekkürler! Bu bileşen
harita

10

Yeni bir çözüm buldum. ES6 modüllerini kullandığımı ve bu nedenle sınıfa ihtiyacım olduğunu unutmayın. Bunun yerine yeni bir React sınıfı da tanımlayabilirsiniz.

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

var type = "example";

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

1
@klinore Özelliğe erişmeye çalıştınız mı default? ie: requir ('./ SampleComponent'). varsayılan?
Khanh Hua

7

Bileşenleriniz globalse şunları yapabilirsiniz:

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


1
Bu, özellikle Rails kullanılıyorsa güzel çalışır. ComponentsDizi tanımlanmadığı için kabul edilen cevap benim için çalışmıyor .
Vadim

3
Genel olarak (euw) rasgele adlandırılmış nesneler eklemek yerine, kaydedebileceğiniz bir bileşen kayıt defteri tutmayı ve gerektiğinde bileşen referanslarını almayı düşünmelisiniz.
slashwhatever

6

Bir sargı bileşeni için basit bir çözüm React.createElementdoğrudan kullanmaktır (ES6 kullanarak).

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


2

Bileşen haritalarıyla tüm seçenekler arasında ES6 kısa sözdizimini kullanarak haritayı tanımlamanın en basit yolunu bulamadım:

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

const components = {
    PhotoStory,
    VideoStory,
}

function Story(props) {
    //given that props.story contains 'PhotoStory' or 'VideoStory'
    const SpecificStory = components[props.story]
    return <SpecificStory/>
}

1

Bir haritaya sahip olmak, büyük miktarda bileşenle hiç iyi görünmüyor. Aslında kimsenin böyle bir şey önermediğine şaşırdım:

var componentName = "StringThatContainsComponentName";
const importedComponentModule = require("path/to/component/" + componentName).default;
return React.createElement(importedComponentModule); 

Bir json dizisi şeklinde yüklü bileşenleri oldukça büyük miktarda render gerektiğinde bu gerçekten bana yardımcı oldu.


Bu benim için işe yaradı ve beni doğru yöne götürdü. Çağırmaya çalışmak React.createElement(MyComponent)doğrudan bir hata attı. Özellikle, üst bir bileşen (bir haritalama) almak için tüm bileşenleri bilmek zorunda istemiyorum - bu ekstra bir adım gibi görünüyor çünkü. Bunun yerine kullandım const MyComponent = require("path/to/component/" + "ComponentNameString").default; return <MyComponent />.
semaj1919

0

Aşağıdaki bileşen, dinamik bileşen yüklemesi ile çeşitli görünümlere erişmek istiyoruz. Aşağıdaki kod, bir url'nin arama dizesinden ayrıştırılmış bir dize kullanarak bunun nasıl yapılacağına dair çalışan bir örnek verir.

Bu url yollarını kullanarak bir sayfaya 'snozberrys' iki benzersiz görünümle erişmek istediğimizi varsayalım:

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

ve

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

görüşümüzün denetleyicisini şöyle tanımlıyoruz:

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

Varsayalım flag, stateveya ' dan farklı değil props:

import ComponentOne from './ComponentOne';
import ComponentTwo from './ComponentTwo';

~~~

const Compo = flag ? ComponentOne : ComponentTwo;

~~~

<Compo someProp={someValue} />

Bayrakla veya Compoile doldurun ve sonra bir React Bileşeni gibi davranabilir.ComponentOneComponentTwoCompo


-1

Her zaman gerçek bileşenlerimizi bildiğimiz için biraz farklı bir yaklaşım kullandım, bu yüzden anahtar kasasını uygulamayı düşündüm. Ayrıca benim durumumda toplam bileşen sayısı 7-8 civarındaydı.

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

    }
  }

Sadece bununla tekrar karşılaştım. Bunu yapmanın yolu bu. Bileşenlerinizi her zaman zaten biliyorsunuz ve yüklemeniz gerekiyor. Bu harika bir çözüm. Teşekkürler.
Jake

-1

Düzenleme: Diğer yanıtlar daha iyidir, yorumlara bakın.

Aynı sorunu bu şekilde çözdüm:

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

3
Muhtemelen bunu yapmak istemezsiniz çünkü React.createElementbir kerede yalnızca bir tanesi işlense bile, arama nesnenizdeki her bileşen için çağrılacaktır. Daha da kötüsü, arama nesnesini renderüst bileşenin yöntemine koyarak, üst öğe her oluşturulduğunda bunu tekrar yapar. En iyi yanıtlar aynı şeyi başarmanın çok daha iyi bir yoludur.
Inkling

2
@Inkling, katılıyorum. Bu, React ile yeni başladığım zamandı. Bunu yazdım, sonra daha iyi bildiğimde her şeyi unuttum. Bunu işaret ettiğiniz için teşekkürler.
Hammad Akhwand
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.