noblerare noblerare - 10 days ago 5
React JSX Question

Pass data through levels - open Modal dialog at the end

I've been pounding my head on this for many hours and could use some help.

Fundamentally, what I am trying to do is that I have two layers of components - the last of which is supposed to open up a Modal Dialog when you click it. Because of React's idea that a component should only change its own state, I want to propagate that data up and set that variable.

Here is the layout. The

FirstLevel.jsx
file is the top of my hierarchy. It is followed by
SecondLevel.jsx
and
ThirdLevel.jsx
which is where the actual text is clicked.

I don't know about the syntax on anything. Not sure if
onUserInput
is the right attribute to use or
handleUserClick
is a built-in thing or a user-defined thing. The idea here is that I am trying to propagate the callback function
handleUserClick
down into the
SecondLevel
. Is this right so far?

FirstLevel.jsx

export default class FirstLevel extends React.Component {
constructor(props) {
super(props);
this.state = {
dialogActive: ''
};
this.handleUserClick = this.handleUserClick.bind(this);
}

handleUserClick(dialogActive) {
this.setState({
dialogActive: dialogActive
});
}

render() {
<SecondLevel onUserInput={this.handleUserClick}/>
}


Now, on the
SecondLevel
, I propagate the callback function even further down into the
ThirdLevel
. Is this the right way to do it so far?

SecondLevel.jsx

render () {
//other logic and tags before this

<ThirdLevel onUserInput={this.props.onUserInput}/>
}


Now this level is where all hell breaks loose and I have no idea what I am doing. On the click, I want to set the
dialogActive
variable that was propagated down and then let that float back up. I still don't know if
onUserInput
is the right thing to do or if the parameter is even correct. Everything is very hazy because it was just gotten by following tutorials and doing lots of Googling and throwing in bits and pieces from everywhere.

ThirdLevel.jsx

export default class ThirdLevel extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}

handleClick() {

this.props.onUserInput(
this.dialogActive.value
);

//show Modal dialog somehow
}

render() {

return <text ref={(input) => this.dialogActive = true} onClick={this.handleClick}> {this.props.value}</text>;
}


Finally, I want to show some modal dialog. Clicking the text needs to reveal a modal dialog. The modal dialog is in another component called
MyModal.jsx


In the
ThirdLevel
, I've tried importing
MyModal
and tried calling the
showModal
function. Didn't work. Then, I tried doing some
React.createElement(MyModal)
stuff and rendering it but that didn't work. All kind of other things that I forgot and just trying stuff until it works but it didn't. What am I doing wrong?

MyModal.jsx

export default class MyModal extends React.Component {
constructor(props) {
super(props);
this.state = {show: false};

this.showModal = this.showModal.bind(this);
this.hideModal = this.hideModal.bind(this);

}

showModal() {
this.setState({show: true});
}

hideModal() {
this.setState({show: false});
}

render() {

return (
<Modal
{...this.props}
show={this.state.show}
onHide={this.hideModal}
dialogClassName={styles.largeDialogBox}
>
//more modal stuff here
);
}
}


Big picture: trying to propagate a click action back up to the top of the hierarchy to set some state and that click action needs to open a modal dialog. Any help would be greatly appreciated.

Edit

Do I do something like this in my
ThirdLevel
?

handleClick() {

this.props.onUserInput(
this.dialogActive.value
);

//show Modal dialog somehow
var newmodal = new MyModal(this.props);
React.render(React.createElement(newModal));
}

render() {
return <text onClick={this.handleClick}> {this.props.value}</text>;
}


Edit 2

My
ThirdLevel
render function returns this:

<div>
<MyModal isDialogActive={this.props.dialogActive} onHideModal={this.props.onUserInput}/>
<tspan onClick={this.handleClick}> {this.props.value} </tspan>
</div>


When that gets passed back up into the
SecondLevel
, it becomes:

<text>
<div>
<MyModal isDialogActive={this.props.dialogActive} onHideModal={this.props.onUserInput}/>
<tspan onClick={this.handleClick}> {this.props.value} </tspan>
</div>
</text>


It's weird to wrap things in the
div
but that's the only way to make the render work. Even though the resulting DOM has all the tags there, none of the actual
tspan
s are showing.

Answer

I believe this will get you on the right path.

I would suggest refactoring the name of some of your functions as it does get a bit confusing. handleUserClick then onUserInput etc. But you've already mentioned that in your OP.

// First Level
export default class FirstLevel extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            dialogActive: false
        };
        this.handleUserClick = this.handleUserClick.bind(this);
    }

  handleUserClick(dialogActive) {
      this.setState({
          dialogActive: dialogActive
      });
  }

  render() {
      <SecondLevel onUserInput={this.handleUserClick}/>
  }
}

// Second Level
...
render () {
    //other logic and tags before this
    <ThirdLevel onUserInput={this.props.onUserInput}/>
}
...

// Third Level
export default class ThirdLevel extends React.Component {
    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
      this.props.onUserInput(true);
    }

    render() {

        return (
          <div>
            <MyModal isDialogActive={this.props.dialogActive} onHideModal={this.props.onUserInput} />
            <button onClick={this.handleClick}>Show Modal</button>
          </div>
        )
    }
}

// Modal
export default class MyModal extends React.Component {
    constructor(props) {
        super(props);
        this.hideModal = this.hideModal.bind(this);
    }

    hideModal() {
      this.props.onUserInput(false);
    }

    render() {
        return (
                <Modal
                    {...this.props}
                    show={this.props.isDialogActive}
                    onHide={this.hideModal}
                    dialogClassName={styles.largeDialogBox}
                >
                //more modal stuff here
           );
     }
}

However I would be asking why you need this logic in the FirstLevel and it cannot be further down the component tree.

Comments