user2696466
user2696466

Reputation: 740

dynamic loading of react components from string name

I have to load different react component for different input types. I dont want to use switch case as this will become very huge. So i have created a map for each type and component to be loaded and using a variable to populate the final render component name. But this is not working.

const FIELD_COMPONENTS_CLASSES_MAP = {
    text: 'FieldsComponent',
    phone: 'FieldsComponent',
    email: 'FieldsComponent',
    decimal: 'FieldsComponent',
    date: 'FieldsComponent',
    datetime: 'FieldsComponent',
    location: 'FieldsComponent',
    meeting: 'FieldsComponent',
    number: 'FieldsComponent',
    multi_select_check_box: 'FieldsComponent',
    code_name_spinner: 'FieldsComponent',

};
export default class FieldsFactoryComponent extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.state = {field: this.props.field, options: this.props.options};
    }

    componentWillReceiveProps(nextProps) {
        this.setState({field: nextProps.field, options: nextProps.options});
    }

    render() {
        let Component = 'FieldsComponent';
        if(this.state.field) {
            Component = FIELD_COMPONENTS_CLASSES_MAP[this.state.field._data.type]
        }

        return (this.state.field ?
            <Component field={this.state.field} options={this.state.options}
                             onSave={this.props.onSave}> </Component> : <div className="hidden"></div>)
    }
}

right now example show only one component, because i was testing if this approach works or not. what I am missing here.

Upvotes: 1

Views: 1260

Answers (2)

Pranesh Ravi
Pranesh Ravi

Reputation: 19113

You need to assign the component itself as the value to the key. See the example.

Hope it helps!

class A extends React.Component{
  render(){
    return <h1>Hello! I'm A</h1>
  }
}

class B extends React.Component{
  render(){
    return <h1>Hello! I'm B</h1>
  }
}

const map = {
  A, //equal to A: A
  B, //equal to B: B
}

class App extends React.Component{
  constructor(){
    super()
    this.onChange = this.onChange.bind(this)
    this.state = {
      component: 'A'
    }
  }
  onChange(e){
    this.setState({
      component: e.target.value
    })
  }
  
  render(){
    const Component = map[this.state.component || 'A']
    return <div>
      <select onChange={this.onChange} selected={this.state.component}>
        <option value="A">A</option>
        <option value="B">B</option>
      </select>
     <Component/>
    </div>
  }
}

ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

Upvotes: 0

Alexandr Lazarev
Alexandr Lazarev

Reputation: 12882

FIELD_COMPONENTS_CLASSES_MAP[this.state.field._data.type] returns string, not a component object.

Your FIELD_COMPONENTS_CLASSES_MAP has should have references to the component objects, not their string names.

const FieldsComponent = props => {
  return (
    <p>Foo</p>
  )
} 

const FIELD_COMPONENTS_CLASSES_MAP = {
    text: FieldsComponent,
    phone: FieldsComponent,
    email: FieldsComponent,
    //...
};

Upvotes: 3

Related Questions