Patrick Dench
Patrick Dench

Reputation: 921

Rendering a dynamic child component in React

I have a menu component that should be dynamically loading element in another container component. However, the child elements are never rendered.

App.js

import React, { Component } from 'react';
import './App.css';
import Menu from './components/navigation/Menu';
import Header from './components/navigation/Header';
import Container from './components/navigation/Container';

class App extends Component {
 render() {
    return (
      <div className="App" >
        <Header />
        <Menu OnMenuChange={(appName) => { this.setState({ currentApp: appName }) }} />
        <Container App={this.state.currentApp}/>
      </div>
    );
  }
}


export default App;

Container.js

import React from 'react';
import './container.css';
import { MyApp } from '../../apps';

class Container extends React.Component {

    render() {
        return (
            <div className="container">
                { this.props.App ? <this.props.App /> : null }
            </div>
        );
    }
}

export default Container;

When the user clicks on a menu option, it triggers OnMenuChange back at the app.js level, which updates the Container component. Which then puts a "MyApp" element in the container div...

But the constructor and render method are never called in the MyApp component.

Update I moved the list of menu items into the App.js file:

let navGroups = [
  {
    links: [
      {
        key: "myApp",
        name: "My App",
        type: MyApp,
      }
    ]
  }
];

Then pass that list into the menu which now passes the 'type' back rather than the name. This works better as the name can now have spaces in it.

Upvotes: 0

Views: 1937

Answers (2)

Jordan Running
Jordan Running

Reputation: 106027

Let's consider what this bit of JSX transpiles to:

<this.props.App />

React.createElement(this.props.App, null)

...and now review what React.createElement does. From the docs:

createElement()

React.createElement(
  type,
  [props],
  [...children]
)

Create and return a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span'), or a React component type (a class or a function).

From the looks of it, this.props.App in your code is a string. Whether that string is 'div' or 'MyFancyApp', React assumes it's the name of an HTML tag (because how could it do anything else?).

If you want React to treat this.props.App as a component, the value of this.props.App has to be an actual component class or function—not a string with the name of a component. You could change the Menu component to pass components instead of strings to OnMenuChange, or you could make a change like this to the App component:

import PeopleApp from './PeopleApp';
import PlacesApp from './PlacesApp';

const APPS_BY_NAME = { PeopleApp, PlacesApp };

class App extends Component {
 render() {
    return (
      // ...
      <Container App={APPS_BY_NAME[this.state.currentApp]}/>
      // ...
    );
  }
}

Upvotes: 1

blackeyebeefhorsefly
blackeyebeefhorsefly

Reputation: 1949

I think the issue might be that your state object is undefined when the first render happens and then prevents any sort of update to it. I would probably just set it to an empty object to initialize it.

class MyComponent extends Component {
  state = {
    App: null
  }
}

Example: https://jsfiddle.net/614ykpgg/9/

Upvotes: 1

Related Questions