y'ffre
y'ffre

Reputation: 77

Render a component when another component is clicked

I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. I know how to set state in the component itself, but how do I affect another component when I click a different component?

class BlackSpark extends React.Component {
    render() {
        return (
            <div className="black"></div>
        );
    }
}

class RedSpark extends React.Component {
    render() {
        return (
            <div className="red"></div>
        );
    }
}

class App extends React.Component {
    render() {
        return (
            <div>
             <BlackSpark />
             <RedSpark />
            </div>
        );
    }
}

Upvotes: 2

Views: 5201

Answers (2)

Dwayne Crooks
Dwayne Crooks

Reputation: 2857

I know how to set state in the component itself, but how do I affect another component when I click a different component?

The recommended way to do it would be to create a parent component that has the state. You'd then use that state to determine when to render the other child component.

I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. Also, what if I want to hide BlackSpark when GreenSpark is clicked and GreenSpark is inside BlackSpark?

In this case, here's how you'd do it.

const GreenSpark = ({ onClick }) => (
  <button className="green" onClick={onClick}>X</button>
)

const BlackSpark = ({ onClick }) => (
  <div className="black">
    <GreenSpark onClick={onClick} />
  </div>
)

const RedSpark = ({ onClick }) => (
  <div className="red" onClick={onClick}></div>
)

class Spark extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      showBlack: false
    }

    this.boundShowBlack = this.showBlack.bind(this)
    this.boundHideBlack = this.hideBlack.bind(this)
  }

  showBlack() {
    this.setState({ showBlack: true })
  }

  hideBlack() {
    this.setState({ showBlack: false })
  }

  render() {
    return (
      <div>
        <RedSpark onClick={this.boundShowBlack} />
        {this.state.showBlack && <BlackSpark onClick={this.boundHideBlack} />}
      </div>
    )
  }
}

Upvotes: -1

Andrew Li
Andrew Li

Reputation: 57954

In React, there's a concept of component composition as you've already embraced -- it allows you to accomplish what you want by rendering children based on the parent's state, another key concept known as lifting state up. What this means, is if you have mutually dependent components, create a single parent which composes them, and have state in the parent control the presentation and logic of the children. With the parent App, you can keep your state inside App, and based on App's state, conditionally render whatever you want -- either BlackSpark or both. For example, using the logical && operator:

{condition && <Component />}

This will only render <Component> when condition is truthy, or else it will not render anything at all (except for when condition is 0). Applying it to this situation, try adding state to your App component to utilize conditional rendering.

There's another key concept you need to understand: component props. They are essentially inputs to a component, certain properties passed to the component to tell how it should behave -- like attributes on regular HTML elements such as input placeholders, URLs, and event handlers. For example:

<Component foo="bar" bar={3} />

This will pass the props foo and bar down to Component with the values "bar" and 3 respectively and are accessible through this.props. If you were to access this.props.foo inside the Component component it would give you "bar". If you pair this up with composition, you can accomplish what you want:

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      showHello: true
    }
    this.handleChange = this.handleChange.bind(this);
  }
  
  handleChange() {
    this.setState(prevState => ({
      showHello: !prevState.showHello
    }));
  }

  render() {
    return (
      <div>
        {this.state.showHello && <Child2 />}
        This is a test.
        <Child1 onClick={this.handleChange} />
      </div>
    );
  }
}

class Child1 extends React.Component {
  render() {
    return <div onClick={this.props.onClick}>Click me!</div>
  }
}

class Child2 extends React.Component {
  render() {
    return <div>Hello!</div>
  }
}

ReactDOM.render(<Example />, document.getElementById('root'));
<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="root"></div>

The above example lifts state up by having a parent compose the children and maintain the state. It then uses props to pass down an onClick handler to Child1, so that whenever Child1 is clicked, the state of the parent changes. Once the state of the parent changes, it will use conditional rendering to render <Child2> if the condition is truthy. Further reading at the React documentation and on the logical && operator.

Upvotes: 4

Related Questions