2

I have a situation where I have 3 components; <View> <Content> and <Footer>.

<View> is parent of <Content> and <Footer>. Now there is a button in <Footer> and when it is clicked, I want to render an Overlay in the <Content> component, so i have to have access to this of the <Content> component inside my <Footer> component. I am not getting on how to accomplish that.

View.js

class View extends Component {
  render() {
    return (
      <div>
        <Content messages={this.props.messages}/>
        <Footer />
      </div>
    )
  }
}

Content.js

class Content extends Component {
  render() {
    return (
      <div>
        {this.props.messages}
      </div>
    )
  }

}

Footer.js

class Footer extends Component {
  constructor(props,context){
    super(props,context);
    this.state = {
      showEmojiPicker: false,
    }
  }

  handleToggleEmojiPicker() {
    this.setState({
      showEmojiPicker: !this.state.showEmojiPicker,
    });
  }

  render() {
    return (
      <div>
        <div>
          <div>
            <bootstrap.Button ref="targetEmojiPicker" bsSize="xsmall" onClick={this.handleToggleEmojiPicker.bind(this)}>
              <bootstrap.Glyphicon glyph="heart" />
            </bootstrap.Button>
              <bootstrap.Overlay
                  show={this.state.showEmojiPicker}
                  onHide={this.handleClose.bind(this)}
                  container={content.this}  //Need this of Content component
                  target={() => ReactDOM.findDOMNode(this.refs.targetEmojiPicker)}
              >
              <EmojiModalCategories actions={this.props.actions}/>
            </bootstrap.Overlay>
          </div>
        </div>
      </div>

    )
  }

}
iamsaksham
  • 2,879
  • 4
  • 26
  • 50
  • 1
    *"so i have to access the `this` of `` component inside my `
    ` component."* No. Instead, `Footer` should accept a callback to notify its "parent" (``) that the button was clicked. `` can then update its state, rerender and pass different props or children to `` that make it render the overlay.
    – Felix Kling Apr 27 '16 at 12:13

3 Answers3

5

You should have a read at the Communicate between components page of the official React documentation.

If you want to access a function in the Content component from the Footer you'll have to supply that function as a prop to your Footer from the View.

Assume you want to run the showOverlay() function that's in your Content component, you could do:

View

displayOverlay = () => {
  this.refs.content.showOverlay();
}

render() {
  //some code
  <Content ref="content" />
  //some code
  <Footer overlayButtonClick={this.displayOverlay} />
}

Footer

btnClick = () => {
  this.props.overlayButtonClick();
}    

render() {
  //some code
  <button onClick={this.btnClick}>Click</button>
  //some code
}

Edit August 2017

Note that using string literals for ref value is now deprecated. For more info, you may have a look at an earlier answer I made, here.

Chris
  • 57,622
  • 19
  • 111
  • 137
1

If you are using Redux Or Flux better approach would be dispatch some action 'DISPLAY_OVERLAY'. change some reducer or store value to true for example reducer.displayOverlay=true; and then use that reducer value in your Content component's render method.

  render:function(){
    return <div>
    {reducer.displayOverLay? this.showOverlay():this.hideOverlay()}
    </div>
    } 

if you are not using flux or redux then make use of your view component state. set a state variable showOverlay:false in your view components state. then pass that showOverlay down to Content component

<Content showOverlay={this.state.showOverlay}/>

then

View

toggleOverLay(){
   this.setState({
       showOverlay:!this.state.showOverlay
    })
}
render() {
  //some code
  <Content showOverlay={this.state.showOverlay}/>
  //some code
  <Footer toggleOverlay={this.toggleOverlay} />
}

Footer

 btnClick = () => {
  this.props.toggleOverlay();
}    

render() {
  //some code
  <button onClick={this.btnClick}>Click</button>
  //some code
}
pashaplus
  • 3,596
  • 2
  • 26
  • 25
  • Actually I can have multiple chat windows. The chat window is parent component called and it have 2 child where messages are displayed and
    where i have textbox and smiley button. I need that when the smiley button in
    is clicked the overlay with smileys open in the same chat window's component, so using store in this will make it quite bulky as i have to store states for every chat window which can be as many
    – iamsaksham Apr 27 '16 at 13:12
0

By doing so I got access to 'this' of <Content> in <Footer> component. Now I am not sure if morally I am doing the wrong thing, hurting the Reactjs motive of one-way binding. I'm using redux too, but how can I approach this with the help of store. Suggestions are highly appreciable coz i'm literally confused

View.js

class View extends Component {

  constructor(props,context){
    super(props,context);
    this.state = {
      thisOfContent: false    
    }
  }

  handleUpdateValue(newThisOfContent) {
    this.setState({
      thisOfContent: newThisOfContent
    })  
  }

  render() {
    return (
      <div>
        <Content handleUpdateValue={this.handleUpdateValue.bind(this)}/>
        <Footer stateValue={this.state.thisOfChatWindowContent}/>
      </div>
    )
  }

}

Content.js

class Content extends Component {

  componentDidMount() {
    this.props.handleUpdateValue(this);
  }

  render() {
    return (
      <div>

      </div>
    )
  }

}

Footer.js

class Footer extends Component {
  constructor(props,context){
    super(props,context);
    this.state = {
      showEmojiPicker: false,
    }
  }

  handleToggleEmojiPicker() {
    this.setState({
      showEmojiPicker: !this.state.showEmojiPicker,
    });
  }

  render() {
    return (
      <div>
        <div>
          <div>
            <bootstrap.Button ref="targetEmojiPicker" bsSize="xsmall" onClick={this.handleToggleEmojiPicker.bind(this)}>
              <bootstrap.Glyphicon glyph="heart" />
            </bootstrap.Button>
            <bootstrap.Overlay
                show={this.state.showEmojiPicker}
                onHide={this.handleClose.bind(this)}
                container={this.props.stateValue}
                target={() => ReactDOM.findDOMNode(this.refs.targetEmojiPicker)}
            >
              <EmojiModalCategories actions={this.props.actions}/>
            </bootstrap.Overlay>
          </div>
        </div>
      </div>

    )
  }

}
iamsaksham
  • 2,879
  • 4
  • 26
  • 50