반응 / 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.