Jordan Ratanadawong
Jordan Ratanadawong

Reputation: 131

In React, how would you dynamically render a component?

I'm open to alternative methods of performing the same task.

I'd like to do something along the lines of the following:

Menu Item 1
Menu Item 2
Menu Item 3

Target(Where component is loaded)

Upon clicking on a menu item, render the associated component inside the target. Is there a way to dynamically load a component? I.e.

<{menuItemName} />

My current methodology of doing this is awful, slow, and cumbersome(and doesn't work), and involves

<li onClick=this.setState({menuItem:"MenuItem1"})>Menu Item 1</li>
<li onClick=this.setState({menuItem:"MenuItem2"})>Menu Item 2</li>
<li onClick=this.setState({menuItem:"MenuItem3"})>Menu Item 3</li>

<{this.state.menuItem} />

I know there must be a better method of doing this, but I don't currently have the React vocabulary to find the solution I'm looking for. I also am aware that setting state in the render method is a big no-no, but I'm struggling to find the alternative.

Thanks for your help.

Upvotes: 1

Views: 2498

Answers (2)

VincentCordobes
VincentCordobes

Reputation: 117

As you suggested you have to store somewhere the selected item; for example in the component's state and then display the corresponding component.

const Component1 = (props) => <div>My component 1</div>;
const Component2 = (props) => <div>My component 2</div>;
const Component3 = (props) => <div>My component 3</div>;

// associate the item id with 
// the corresponding component
const components = {
  1: <Component1 />,
  2: <Component2 />,
  3: <Component3 />,
}

class MyApp extends Component {

  constructor(props) {
    super(props);
    this.state = { selectedItem: 1 }
  }

  handleItemClick(itemId) {
    this.setState({ selectedItem: itemId });
  }

  render() {
    return (
      <div>
        <li onClick={() => this.handleItemClick(1)}>Item 1</li>
        <li onClick={() => this.handleItemClick(2)}>Item 2</li>
        <li onClick={() => this.handleItemClick(3)}>Item 2</li>
        {components[this.state.selectedItem]}
      </div>
    );
  }
}

Alternatively you can store the current selected item in the url using React Router.

Check https://react-router.now.sh/quick-start out.

Upvotes: 1

jssridhar
jssridhar

Reputation: 458

Calling setState in render is wrong, but you are just attaching an event listener. Coming to your issue is a jsx representation which compiles to React.createElement(ReactComponent, {}) where the first argument is a React class (can also be a string for HTML elements). Check the answer on this question React / JSX Dynamic Component Name.

However, to solve your problem you can do something like this

handleEventClick(itemId) {
  return () => {
    this.setState({
     itemId
    });
  }
}

<li onClick={::this.handleEventClick("itemId")}>Item Name</li>

render() {
  const { itemId } = this.state;
  let renderComponent;
  if (itemId === 'menu1') {
    renderComponent = <MenuComponent1 />;
  } else if ....other components

  return (
    ...
    <li onClick={::this.handleEventClick("itemId")></li>...
    {renderComponent}
  );

}

Upvotes: 1

Related Questions