Tên thành phần động React / JSX


167

Tôi đang cố gắng tự động kết xuất các thành phần dựa trên loại của chúng.

Ví dụ:

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

Tôi đã thử giải pháp được đề xuất ở đây Tên thành phần động React / JSX

Điều đó đã cho tôi một lỗi khi biên dịch (sử dụng browserify cho gulp). Nó mong đợi XML nơi tôi đang sử dụng một cú pháp mảng.

Tôi có thể giải quyết điều này bằng cách tạo một phương thức cho mọi thành phần:

newExampleComponent() {
    return <ExampleComponent />;
}

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

Nhưng điều đó có nghĩa là một phương thức mới cho mọi thành phần tôi tạo ra. Phải có một giải pháp thanh lịch hơn cho vấn đề này.

Tôi rất cởi mở để đề xuất.

Câu trả lời:


157

<MyComponent />biên dịch thành React.createElement(MyComponent, {}), dự kiến ​​một chuỗi (thẻ HTML) hoặc hàm (ReactClass) làm tham số đầu tiên.

Bạn chỉ có thể lưu trữ lớp thành phần của mình trong một biến có tên bắt đầu bằng chữ in hoa. Xem các thẻ HTML so với các thành phần React .

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

biên dịch thành

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

5
Độc giả tương lai cũng có thể sẽ thấy {...this.props}hữu ích để chuyển các đạo cụ trong suốt sang các thành phần được phân nhóm từ cha mẹ. Giống nhưreturn <MyComponent {...this.props} />
Dr.Strangelove

4
Cũng đảm bảo rằng bạn viết hoa tên biến động của bạn.
saada

28
Hãy nhớ rằng biến của bạn nên giữ chính thành phần đó chứ không chỉ tên của thành phần dưới dạng chuỗi .
totymedli

3
Nếu bạn cũng thắc mắc tại sao var cần được viết hoa facebook.github.io/react/docs/iêu
Nobita

3
Các thành phần không được xác định
ness-EE

144

Có một tài liệu chính thức về cách xử lý các tình huống như vậy có sẵn tại đây: https://facebook.github.io/react/docs/jsx-in-depth.html#ch rủi-the-type-at-r nb

Về cơ bản nó nói:

Sai lầm:

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

Chính xác:

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
Điều RẤT QUAN TRỌNG: một biến viết hoa
mpyw

4
Bên cạnh thực tế đó là tài liệu chính thức, nó dễ dàng là câu trả lời tốt nhất và giải pháp có cấu trúc nhất. Có lẽ đó là lý do tại sao trong tài liệu :)
domdambrogia

1
Cảm ơn câu trả lời tuyệt vời. Đối với những độc giả sau, lưu ý rằng giá trị trong đối tượng bản đồ (đối tượng bản đồ ở đây là các thành phần const và giá trị là PhotoStory và VideoStory) phải không có dấu ngoặc kép - Nếu không, thành phần sẽ không hiển thị và bạn sẽ gặp lỗi - trong tôi trường hợp tôi đã bỏ lỡ nó và chỉ lãng phí thời gian ...
Erez Lieberman

11

Cần có một thùng chứa ánh xạ tên thành phần cho tất cả các thành phần được cho là được sử dụng động. Các lớp thành phần nên được đăng ký trong một thùng chứa bởi vì trong môi trường mô đun, nếu không thì không thể truy cập một nơi duy nhất. Các lớp thành phần không thể được xác định bằng tên của chúng mà không chỉ định rõ ràng vì hàm nameđược thu nhỏ trong sản xuất.

Bản đồ thành phần

Nó có thể là đối tượng đơn giản:

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

Hoặc Mapví dụ:

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

Đối tượng đơn giản là phù hợp hơn vì nó được hưởng lợi từ tốc ký tài sản.

Mô-đun thùng

Một mô-đun thùng với xuất khẩu được đặt tên có thể hoạt động như bản đồ:

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

Điều này hoạt động tốt với một lớp cho mỗi kiểu mã mô-đun.

Người trang trí

Trình trang trí có thể được sử dụng với các thành phần lớp cho đường cú pháp, điều này vẫn yêu cầu xác định rõ ràng tên lớp và đăng ký chúng trong bản đồ:

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

Một bộ trang trí có thể được sử dụng như một thành phần bậc cao hơn với các thành phần chức năng:

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

export default dynamic(Bar);

Việc sử dụng phi tiêu chuẩndisplayName thay vì thuộc tính ngẫu nhiên cũng có lợi cho việc gỡ lỗi.


Cảm ơn! Thành phần đó là sạch sẽ và tốt đẹp :)
Leon Gaban

10

Tôi đã tìm ra một giải pháp mới. Xin lưu ý rằng tôi đang sử dụng các mô-đun ES6 vì vậy tôi đang yêu cầu lớp học. Bạn cũng có thể định nghĩa một lớp React mới thay thế.

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

var type = "example";

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

1
@klinore Bạn đã thử truy cập defaultthuộc tính? tức là: yêu cầu ('./ exampleComponent'). mặc định?
Khánh Hua

7

Nếu các thành phần của bạn là toàn cầu, bạn chỉ cần làm:

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


1
Điều này hoạt động rất đẹp, đặc biệt là nếu sử dụng Rails. Câu trả lời được chấp nhận không hoạt động đối với tôi, vì Componentsmảng không được xác định.
Vadim

3
Thay vì gắn các đối tượng được đặt tên tùy ý vào phạm vi toàn cầu (euw), bạn nên xem xét việc duy trì sổ đăng ký thành phần mà bạn có thể đăng ký và sau đó truy xuất các tham chiếu thành phần từ khi cần.
slashwhthing

6

Đối với một thành phần bao bọc, một giải pháp đơn giản sẽ chỉ là sử dụng React.createElementtrực tiếp (sử dụng 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 })
    )
  }
}

Tôi thực sự có một thành phần có cùng mục đích trong dự án của mình)
Dziamid

2

Trên tất cả các tùy chọn với bản đồ thành phần Tôi chưa tìm thấy cách đơn giản nhất để xác định bản đồ bằng cú pháp ngắn 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/>
}

1

Có một bản đồ trông không đẹp chút nào với một lượng lớn các thành phần. Tôi thực sự ngạc nhiên khi không ai đề xuất một cái gì đó như thế này:

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

Điều này đã thực sự giúp tôi khi tôi cần kết xuất một số lượng lớn các thành phần được tải dưới dạng mảng json.


Điều này gần với những gì làm việc cho tôi, và đưa tôi đi đúng hướng. Cố gắng gọi React.createElement(MyComponent)trực tiếp đã ném một lỗi. Cụ thể, tôi không muốn phụ huynh phải biết tất cả các thành phần để nhập (trong ánh xạ) - vì đó có vẻ như là một bước bổ sung. Thay vào đó, tôi đã sử dụng const MyComponent = require("path/to/component/" + "ComponentNameString").default; return <MyComponent />.
semaj1919

0

Có nghĩa là chúng tôi muốn truy cập các chế độ xem khác nhau với tải thành phần động. Đoạn mã sau đây đưa ra một ví dụ hoạt động về cách thực hiện điều này bằng cách sử dụng một chuỗi được phân tích cú pháp từ chuỗi tìm kiếm của url.

Giả sử chúng tôi muốn truy cập một trang 'snozberry' với hai chế độ xem duy nhất bằng các đường dẫn url này:

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

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

chúng tôi xác định bộ điều khiển xem của chúng tôi như thế này:

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

Giả sử chúng ta có a flag, không khác với statehoặc props:

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

~~~

const Compo = flag ? ComponentOne : ComponentTwo;

~~~

<Compo someProp={someValue} />

Với cờ Compođiền với một ComponentOnehoặc ComponentTwosau đó Compocó thể hoạt động như một Thành phần Phản ứng.


-1

Tôi đã sử dụng một chút Cách tiếp cận khác nhau, vì chúng tôi luôn biết các thành phần thực tế của mình vì vậy tôi nghĩ sẽ áp dụng trường hợp chuyển đổi. Ngoài ra tổng số không có thành phần là khoảng 7-8 trong trường hợp của tôi.

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

    }
  }

Chỉ cần đi qua điều này một lần nữa. Đây là cách để làm điều đó. Bạn luôn biết các thành phần của mình và cần phải tải chúng. Vì vậy, đây là một giải pháp tuyệt vời. Cảm ơn.
Jake

-1

Chỉnh sửa: Các câu trả lời khác là tốt hơn, xem ý kiến.

Tôi đã giải quyết vấn đề tương tự theo cách này:

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

3
Bạn có thể không muốn làm điều này bởi vì React.createElementsẽ được gọi cho mọi thành phần trong đối tượng tra cứu của bạn, mặc dù chỉ một trong số chúng được hiển thị tại một thời điểm. Tồi tệ hơn, bằng cách đặt đối tượng tra cứu vào renderphương thức của thành phần cha, nó sẽ thực hiện lại mỗi khi cha mẹ được kết xuất. Các câu trả lời hàng đầu là một cách tốt hơn để đạt được điều tương tự.
Inkling

2
@Inkling, tôi đồng ý. Đây là khi tôi mới bắt đầu với React. Tôi đã viết điều này, sau đó quên tất cả về nó khi tôi biết rõ hơn. Cảm ơn đã chỉ ra rằng.
Hammad Akhwand
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.