36

We have been experiencing some problems in using react now but it kinda boils to one part of how we have been using react.

How should we have been showing/hiding child components?

This is how we have coded it (this are only snippets of our components)...

_click: function() {
  if ($('#add-here').is(':empty'))
    React.render(<Child />, $('#add-here')[0]);
  else
    React.unmountComponentAtNode($('#add-here')[0]);
},
render: function() {
  return(
    <div>
      <div onClick={this._click}>Parent - click me to add child</div>
      <div id="add-here"></div>
    </div>
  )
}

and lately I've been reading examples like it should've been somewhere along this lines:

getInitialState: function () {
  return { showChild: false };
},
_click: function() {
  this.setState({showChild: !this.state.showChild});
},
render: function() {
  return(
    <div>
      <div onClick={this._click}>Parent - click me to add child</div>
      {this.state.showChild ? <Child /> : null}
    </div>
  )
}

Should I have been using that React.render()? It seems to stop various things like shouldComponentUpdate to cascade to child and things like e.stopPropagation...

index
  • 3,697
  • 7
  • 36
  • 55
  • 1
    Can you explain more detail what the problem with your 2nd solution is? That is actually the preferred way to do. Have a state attribute `showChild` that you toggle when your `div` is clicked. Calling `setState` then rerenders your component. – Lars Blumberg Apr 28 '15 at 07:59
  • @LarsBlumberg. Oh. I actually haven't had experience yet with the 2nd solution. I have only tried it in a small app and seems to work. We have been using the first one since it was what have been taught to us before. But now we have been experiencing problems like the `stopPropagation` not working or we need to manually update Child when `shouldComponentUpdate` triggers on parent. But I haven't really seen any documents regarding this "preferred way", only examples of various tutorials. So should we have been using the 2nd solution? – index Apr 28 '15 at 08:03
  • 1
    Yes, the 2nd is the better choice. The React docs and examples don't use JQuery. The docs have a lot of great info. https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html – WiredPrairie Apr 28 '15 at 10:43
  • @WiredPrairie. Well, I could've still used `this.refs` for that, I just quickly wrote it in jQuery. – index Apr 28 '15 at 10:47
  • 1
    Just don't use jQuery at all or as minimally as possible.. Any time you manipulate the DOM, you run the risk of React flipping out on you. Usually blows errors similar to: `unable to find component at root ''` – Duncan Finney Apr 28 '15 at 10:50
  • You could just do this: https://noobieprogrammer.blogspot.com/2020/09/reactjs-how-to-create-toggle-showhide.html – doppelgunner Sep 26 '20 at 12:34

2 Answers2

66

I've provided a working example that follows your second approach. Updating the component's state is the preferred way to show/hide children.

Given you have this container:

<div id="container">
</div>

you can either use modern Javascript (ES6, first example) or classic JavaScript (ES5, second example) to implement the component logic:

Show/hide components using ES6

Try this demo live on JSFiddle

class Child extends React.Component {
  render() {
    return (<div>I'm the child</div>);
  }
}

class ShowHide extends React.Component {
  constructor() {
    super();
    this.state = {
      childVisible: false
    }
  }

  render() {
    return (
      <div>
        <div onClick={() => this.onClick()}>
          Parent - click me to show/hide my child
        </div>
        {
          this.state.childVisible
            ? <Child />
            : null
        }
      </div>
    )
  }

  onClick() {
    this.setState(prevState => ({ childVisible: !prevState.childVisible }));
  }
};

React.render(<ShowHide />, document.getElementById('container'));

Show/hide components using ES5

Try this demo live on JSFiddle

var Child = React.createClass({
  render: function() {
    return (<div>I'm the child</div>);
  }
});

var ShowHide = React.createClass({
  getInitialState: function () {
    return { childVisible: false };
  },

  render: function() {
    return (
      <div>
        <div onClick={this.onClick}>
          Parent - click me to show/hide my child
        </div>
        {
          this.state.childVisible
            ? <Child />
            : null
        }
      </div>
    )
  },

  onClick: function() {
    this.setState({childVisible: !this.state.childVisible});
  }
});

React.render(<ShowHide />, document.body);
Lars Blumberg
  • 19,326
  • 11
  • 90
  • 127
  • 1
    This is really nice. I would like to implement this. However I also need to remove the Child, if clicked other than the Child component. Could you please guide me how? – Karl Jul 02 '16 at 08:51
  • @LarsBlumberg Yes, I have asked [here](http://stackoverflow.com/questions/38158005/insert-a-component-when-clicked-and-remove-the-componet-when-click-other-than-th). Could you please help me. Thank you. – Karl Jul 02 '16 at 09:31
  • 1
    Just a note on ternary `something ? : null`, why not use `something && ()` instead? Noticed this yesterday and I like it a lot – MrMesees Apr 29 '18 at 05:22
  • @MrMesees I like the && syntax for this case, too. – Lars Blumberg Apr 29 '18 at 08:37
  • @LarsBlumberg I am new to React and not understanding this core concept. If `` is a component shared among many different ``, with this design, each parent would have to implement showing and hiding methods creating tons of code duplication. I have considered to using `refs` in my projects to avoid this problem (`this.child.current.hide()`) however this seems like bad practice. Do you have any solutions to the code duplication problem? – JBis May 11 '19 at 18:58
  • @JBis You could have different "Parent" components implement the showing/hiding of their children. There are different solutions to avoid code duplication, however in this case the implementation is so trivial that I would rather re-implement this show/hide behavior in the Child-containing parent components. – Lars Blumberg May 12 '19 at 10:54
0
    /* eslint-disable jsx-a11y/img-has-alt,class-methods-use-this */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import todoStyle from 'src/style/todo-style.scss';
import { Router, Route, hashHistory as history } from 'react-router';
import Myaccount from 'src/components/myaccount.jsx';

export default class Headermenu extends Component {

  constructor(){
  super();

  // Initial state
  this.state = { open: false };

}

toggle() {
  this.setState({
    open: !this.state.open
  });
}

  componentdidMount() {
    this.menuclickevent = this.menuclickevent.bind(this);
    this.collapse = this.collapse.bind(this);
    this.myaccount = this.myaccount.bind(this);
    this.logout = this.logout.bind(this);
  }

  render() {
    return (
      <div>

        <div style={{ textAlign: 'center', marginTop: '10px' }} id="menudiv" onBlur={this.collapse}>
          <button onClick={this.toggle.bind(this)} > Menu </button>

          <div id="demo" className={"collapse" + (this.state.open ? ' in' : '')}>
            <label className="menu_items" onClick={this.myaccount}>MyAccount</label>
            <div onClick={this.logout}>
              Logout
            </div>
          </div>

        </div>
      </div>
    );
  }

  menuclickevent() {
    const listmenu = document.getElementById('listmenu');
    listmenu.style.display = 'block';
  }



  logout() {
    console.log('Logout');
  }
  myaccount() {
    history.push('/myaccount');
    window.location.reload();

  }


}
arvind grey
  • 150
  • 12