React / JSX Dynamic Component Name

168

Saya mencoba membuat komponen secara dinamis berdasarkan jenisnya.

Sebagai contoh:

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

Saya mencoba solusi yang diusulkan di sini Bereaksi / JSX nama komponen dinamis

Itu memberi saya kesalahan saat kompilasi (menggunakan browserify untuk tegukan). Diharapkan XML tempat saya menggunakan sintaks array.

Saya bisa menyelesaikan ini dengan membuat metode untuk setiap komponen:

newExampleComponent() {
    return <ExampleComponent />;
}

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

Tetapi itu berarti metode baru untuk setiap komponen yang saya buat. Harus ada solusi yang lebih elegan untuk masalah ini.

Saya sangat terbuka untuk saran.

Sam
sumber

Jawaban:

157

<MyComponent />mengkompilasi ke React.createElement(MyComponent, {}), yang mengharapkan string (tag HTML) atau fungsi (ReactClass) sebagai parameter pertama.

Anda bisa menyimpan kelas komponen Anda dalam variabel dengan nama yang dimulai dengan huruf besar. Lihat tag HTML vs Komponen Bereaksi .

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

kompilasi ke

var MyComponent = Components[type + "Component"];
return React.createElement(MyComponent, {});
Alexandre Kirszenberg
sumber
5
Pembaca yang akan datang juga mungkin akan menemukan {...this.props}manfaat untuk secara transparan meneruskan alat peraga ke komponen subtipe dari orang tua. Sepertireturn <MyComponent {...this.props} />
Dr.Strangelove
4
Juga pastikan Anda menggunakan nama variabel dinamis Anda.
saada
28
Perlu diingat bahwa variabel Anda harus memegang komponen itu sendiri dan bukan hanya nama komponen sebagai string .
totymedli
3
Jika Anda juga bertanya - tanya mengapa var perlu dikapitalisasi facebook.github.io/react/docs/…
Nobita
3
Komponen tidak terdefinisi
ness-EE
144

Ada dokumentasi resmi tentang cara menangani situasi seperti ini tersedia di sini: https://facebook.github.io/react/docs/jsx-in-depth.html#choosing-the-type-at-runtime

Pada dasarnya dikatakan:

Salah:

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

Benar:

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} />;
}
gmfvpereira
sumber
25
HAL SANGAT PENTING: variabel kapital
mpyw
4
Selain fakta bahwa itu adalah dokumen resmi, ini dengan mudah merupakan jawaban terbaik dan solusi paling terstruktur. Mungkin itu sebabnya ada di dokumen
:)
1
Terima kasih atas jawaban Anda. Untuk pembaca berikut, perhatikan bahwa nilai dalam objek peta (objek peta di sini adalah komponen const dan nilainya adalah PhotoStory dan VideoStory) harus tanpa tanda kutip - Jika tidak, komponen tidak akan merender dan Anda akan mendapatkan kesalahan - pada saya Jika saya melewatkannya dan hanya membuang-buang waktu ...
Erez Lieberman
11

Seharusnya ada wadah yang memetakan nama komponen ke semua komponen yang seharusnya digunakan secara dinamis. Kelas komponen harus didaftarkan dalam wadah karena dalam lingkungan modular tidak ada tempat di mana mereka dapat diakses. Kelas komponen tidak dapat diidentifikasi dengan namanya tanpa menentukannya secara eksplisit karena fungsinya namediperkecil dalam produksi.

Peta komponen

Ini bisa berupa objek biasa:

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

Atau Mapcontoh:

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

Objek polos lebih cocok karena diuntungkan oleh steno properti.

Modul barel

Sebuah modul barel dengan ekspor bernama dapat bertindak sebagai peta seperti:

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

Ini berfungsi baik dengan satu kelas per gaya kode modul.

Penghias

Dekorator dapat digunakan dengan komponen kelas untuk gula sintaksis, ini masih membutuhkan untuk menentukan nama kelas secara eksplisit dan mendaftarkannya di peta:

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

Dekorator dapat digunakan sebagai komponen tingkat tinggi dengan komponen fungsional:

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

export default dynamic(Bar);

Penggunaan non-standardisplayName alih - alih properti acak juga menguntungkan debugging.

Estus Flask
sumber
Terima kasih!
ComponMapMap
10

Saya menemukan solusi baru. Perhatikan bahwa saya menggunakan modul ES6 jadi saya membutuhkan kelas. Anda juga bisa mendefinisikan kelas Bereaksi baru sebagai gantinya.

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

var type = "example";

newComponent() {
    return components[type]({ attribute: "value" });
}
Sam
sumber
1
@klinore Apakah Anda mencoba mengakses defaultatribut? yaitu: membutuhkan ('./ ExampleComponent'). default?
Khanh Hua
7

Jika komponen Anda bersifat global, Anda dapat melakukannya:

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

sinar
sumber
1
Ini berfungsi dengan baik, terutama jika menggunakan Rails. Jawaban yang diterima tidak berfungsi untuk saya, karena Componentsarray tidak ditentukan.
Vadim
3
Alih-alih melampirkan objek yang dinamai secara sewenang-wenang ke lingkup global (euw), Anda harus mempertimbangkan mempertahankan registri komponen yang dapat Anda daftarkan dan kemudian mengambil referensi komponen dari saat diperlukan.
potong apa pun
6

Untuk komponen pembungkus, solusi sederhana adalah dengan menggunakan React.createElementlangsung (menggunakan 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 })
    )
  }
}
Ricardo Pedroni
sumber
Saya sebenarnya memiliki komponen dengan tujuan yang sama dalam proyek saya)
Dziamid
2

Di semua opsi dengan peta komponen, saya belum menemukan cara paling sederhana untuk mendefinisikan peta menggunakan sintaks pendek ES6:

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/>
}
Stalinko
sumber
1

Memiliki peta sama sekali tidak terlihat bagus dengan sejumlah besar komponen. Saya sebenarnya terkejut bahwa tidak ada yang menyarankan sesuatu seperti ini:

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

Yang ini benar-benar membantu saya ketika saya perlu membuat sejumlah besar komponen yang dimuat dalam bentuk json array.

Arthur Z.
sumber
Ini dekat dengan apa yang berhasil untuk saya, dan membawa saya ke arah yang benar. Mencoba memohon React.createElement(MyComponent)langsung melemparkan kesalahan. Secara khusus, saya tidak ingin orang tua harus mengetahui semua komponen untuk diimpor (dalam pemetaan) - karena itu sepertinya langkah tambahan. Sebaliknya, saya menggunakan const MyComponent = require("path/to/component/" + "ComponentNameString").default; return <MyComponent />.
semaj1919
0

Anggap kita ingin mengakses berbagai tampilan dengan pemuatan komponen dinamis. Kode berikut memberikan contoh yang bisa digunakan tentang bagaimana melakukannya dengan menggunakan string yang diuraikan dari string pencarian dari url.

Mari kita asumsikan kita ingin mengakses halaman 'snozberry' dengan dua tampilan unik menggunakan jalur url ini:

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

dan

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

kami mendefinisikan pengontrol tampilan kami seperti ini:

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'));
1-14x0r
sumber
0

Asumsikan kita memiliki flag, tidak berbeda dengan stateatau props:

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

~~~

const Compo = flag ? ComponentOne : ComponentTwo;

~~~

<Compo someProp={someValue} />

Dengan flag, Compoisi dengan salah satu ComponentOneatau ComponentTwokemudian Compodapat bertindak seperti Komponen Bereaksi.

Amerika
sumber
-1

Saya menggunakan Pendekatan yang sedikit berbeda, karena kami selalu tahu komponen kami yang sebenarnya jadi saya pikir untuk menerapkan saklar kasus. Juga total tidak ada komponen sekitar 7-8 dalam kasus saya.

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

    }
  }
abhirathore2006
sumber
Baru saja menemukan ini lagi. Inilah cara untuk melakukannya. Anda selalu tahu komponen Anda, dan perlu memuatnya. Jadi ini solusi yang bagus. Terima kasih.
Jake
-1

Sunting: Jawaban lain lebih baik, lihat komentar.

Saya memecahkan masalah yang sama dengan cara ini:

...
render : function () {
  var componentToRender = 'component1Name';
  var componentLookup = {
    component1Name : (<Component1 />),
    component2Name : (<Component2 />),
    ...
  };
  return (<div>
    {componentLookup[componentToRender]}
  </div>);
}
...
Hammad Akhwand
sumber
3
Anda mungkin tidak ingin melakukan ini karena React.createElementakan dipanggil untuk setiap komponen dalam objek pencarian Anda, meskipun hanya satu dari mereka yang ditampilkan pada satu waktu. Lebih buruk lagi, dengan menempatkan objek pencarian dalam rendermetode komponen induk, ia akan melakukannya lagi setiap kali induk diberikan. Jawaban teratas adalah cara yang jauh lebih baik untuk mencapai hal yang sama.
Inkling
2
@Inkling, saya setuju. Saat itulah saya baru memulai dengan React. Saya menulis ini, lalu melupakan semua itu ketika saya tahu lebih baik. Terima kasih telah menunjukkannya.
Hammad Akhwand