반응 / JSX 동적 구성 요소 이름


168

유형에 따라 구성 요소를 동적으로 렌더링하려고합니다.

예를 들면 다음과 같습니다.

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

여기에 제안 된 솔루션을 시도했습니다 .React / JSX 동적 구성 요소 이름

컴파일 할 때 오류가 발생했습니다 (gulp에 browserify 사용). 배열 구문을 사용하고있는 XML을 기대했습니다.

모든 구성 요소에 대한 방법을 만들어서 해결할 수 있습니다.

newExampleComponent() {
    return <ExampleComponent />;
}

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

그러나 그것은 내가 만드는 모든 구성 요소에 대한 새로운 방법을 의미합니다. 이 문제에 대한보다 우아한 해결책이 있어야합니다.

나는 제안에 매우 개방적이다.

답변:


158

<MyComponent />로 컴파일 React.createElement(MyComponent, {})하면 문자열 (HTML 태그) 또는 함수 (ReactClass)가 첫 번째 매개 변수로 필요합니다.

대문자로 시작하는 이름을 가진 변수에 컴포넌트 클래스를 저장할 수 있습니다. HTML 태그와 반응 구성 요소를 참조하십시오 .

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

컴파일

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

5
미래의 독자들은 또한 {...this.props}소품을 하위 유형의 구성 요소로 부모에서 투명하게 전달하는 데 유용 할 것 입니다. 처럼return <MyComponent {...this.props} />
— Dr.Strangelove

4
또한 동적 변수 이름을 대문자로 사용해야합니다.
— saada

28
당신의 마음에 계속 변수가 구성 요소를 보유해야 자체와 하지 와 같은 구성 요소의 이름 만 문자열을 .
— totymedli 2012 년

3
왜 var가 대문자 여야 하는지 궁금하다면 facebook.github.io/react/docs/…
— Nobita

3
구성 요소가 정의되어 있지 않습니다
— ness-EE

144

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

25
아주 중요한 것은하십시오 대문자 변수
— mpyw

4
공식 문서라는 사실 외에도 쉽게 대답하고 가장 체계적인 솔루션입니다. 어쩌면 그것은 문서에 이유의 :)
— domdambrogia

1
좋은 답변 주셔서 감사합니다. 다음 독자의 경우 맵 객체 (여기의 맵 객체는 const 구성 요소이고 값은 PhotoStory 및 VideoStory 임) 내의 값은 따옴표 없이 사용해야합니다. 그렇지 않으면 구성 요소가 렌더링되지 않고 오류가 발생합니다. 경우에 나는 그것을 놓치고 그냥 ... 시간을 낭비
— Erez 리버만을

11

컴포넌트 이름을 동적으로 사용해야하는 모든 컴포넌트에 맵핑하는 컨테이너가 있어야합니다. 구성 요소 클래스는 컨테이너 환경에 등록해야합니다. 모듈 식 환경에서는 액세스 할 수있는 단일 위치가 없기 때문입니다. 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 대신 임의의 속성은 또한 디버깅 혜택을 제공합니다.


감사! 그 componentMap은 깨끗하고 훌륭합니다 :)
— Leon Gaban

10

새로운 해결책을 찾았습니다. ES6 모듈을 사용하고 있으므로 클래스가 필요합니다. 대신 새로운 React 클래스를 정의 할 수도 있습니다.

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

var type = "example";

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

1
@klinore default속성 에 액세스하려고 했습니까 ? 즉 : require ( './ ExampleComponent'). default?
— Khanh Hua

7

구성 요소가 전역 인 경우 간단히 다음을 수행 할 수 있습니다.

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


1
특히 Rails를 사용하는 경우 아름답게 작동합니다. Components배열이 정의되지 않았기 때문에 허용 된 답변이 작동하지 않습니다 .
— Vadim

3
임의로 명명 된 개체를 전역 범위 (euw)에 연결하는 대신 등록 할 수있는 구성 요소 레지스트리를 유지 관리 한 다음 필요할 때 구성 요소 참조를 검색해야합니다.
— 17:01에

6

래퍼 구성 요소의 경우 간단한 해결책은 React.createElementES6을 사용하여 직접 사용하는 것 입니다.

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

나는 실제로 내 프로젝트에서 같은 목적을 가진 컴포넌트를 가지고있다)
— Dziamid

2

구성 요소 맵이있는 모든 옵션에서 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

구성 요소가 많으면지도가 표시되지 않습니다. 나는 아무도 이와 같은 것을 제안하지 않았다는 사실에 놀랐습니다.

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

이것은 json 배열 형태로로드 된 많은 양의 구성 요소를 렌더링해야 할 때 실제로 도움이되었습니다.


이것은 나를 위해 일한 것에 가깝고 올바른 방향으로 인도했습니다. React.createElement(MyComponent)직접 호출하려고 하면 오류가 발생했습니다. 특히, 부모가 (매핑에서) 가져올 모든 구성 요소를 알 필요가 없습니다. 추가 단계처럼 보입니다. 대신에을 사용했습니다 const MyComponent = require("path/to/component/" + "ComponentNameString").default; return <MyComponent />.
— semaj1919

0

다음과 같은 코드는 URL의 검색 문자열에서 구문 분석 된 문자열을 사용하여이를 수행하는 방법에 대한 실제 예제를 제공합니다.

다음 URL 경로를 사용하여 두 개의 고유 한보기로 페이지 'snozberrys'에 액세스하려고한다고 가정합니다.

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

0

우리가이 없다고 가정 flag으로부터, 다르지 state또는 props:

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

~~~

const Compo = flag ? ComponentOne : ComponentTwo;

~~~

<Compo someProp={someValue} />

플래그와 함께 Compo중 하나를 채우기 ComponentOne또는 ComponentTwo다음은 CompoA가 구성 요소 반응과 같은 역할을 할 수 있습니다.


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

    }
  }

이 문제가 다시 발생했습니다. 이것이 방법입니다. 어쨌든 항상 구성 요소를 알고로드해야합니다. 따라서 이것은 훌륭한 솔루션입니다. 감사.
— Jake

-1

편집 : 다른 답변이 더 좋습니다. 주석을 참조하십시오.

이 방법으로 같은 문제를 해결했습니다.

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

3
React.createElement한 번에 하나만 렌더링되는 경우에도 조회 객체의 모든 구성 요소에 대해 호출되므로이 작업을 원하지 않을 것입니다. 더 나쁜 것은 render부모 컴포넌트 의 메소드에 lookup 객체를 넣어서 부모가 렌더링 될 때마다 다시 수행한다는 것입니다. 최고의 답변은 동일한 것을 달성하는 훨씬 더 좋은 방법입니다.
— Inkling

2
@Inkling, 동의합니다. 이것은 방금 React로 시작했을 때였습니다. 나는 이것을 썼다. 그리고 내가 더 잘 알면 그것에 관해 모든 것을 잊었다. 지적 해 주셔서 감사합니다.
— Hammad Akhwand
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.