3

While becoming acquainted with React I stumbled upon the concept of portals in the developer documentation. However, I'm having difficulty understanding how this portal component actually renders on-demand, and how I can pass data to it to populate a modal.

Currently, I have two components that interact with each other: View.js and DataTable.js.

View.js:

const Example = (props) => {
    console.log(props);
    return (
        <div>
            <TopBar />
            <DeploymentsHeader env={props.match.params.env} />
            <PendingDataTable env={props.match.params.env} />
            <DataTable env={props.match.params.env} />
        </div>
    );
}

Now for the DataTable component, a react-table is being rendered. When a user clicks on an individual row, my goal is to have a modal pop up (still unclear to me whether or not this needs to have its own separate component if I'm using React portals) and have it be populated with data that is already bound to the individual row (which I tested and have access too).

The code looks something like this:

<ReactTable
   data={tableData}
   filterable={true}
   getTrProps={this.onRowClick}
       columns={[
          {
            Header: "Header",
            accessor: "service_name"
           },
           ...
           ]}
/>

Now this is the function that gets passed to the table row props and executes on click:

onRowClick = (state, rowInfo) => {
        return {
            onClick: e => {
                console.log('A Tr Element was clicked!');
                console.log(rowInfo.original);
            }
        }
    }

The data that I need is readily available to me in the object rowInfo.original. Now my question is: what is considered the 'correct' or 'best-practice' way to load a modal using a portal when an event such as this onClick trigger executes?

  1. Do I need to have a separate Modal.js component that is actually a portal?
  2. How do I get the data from this onRowClick function transported to this modal portal?

Thanks everyone.

Lukon
  • 255
  • 4
  • 20

2 Answers2

4

You can conditionally render a portal as if it was just another React component. To start, you should separate the modal out into it's own component. Then, you can store the item id or item in state and toggle to let the modal know when to show or not.

onRowClick = (state, rowInfo) => {
    return {
        onClick: e => {
            console.log('A Tr Element was clicked!');
            console.log(rowInfo.original);
            this.setState({
                data: rowInfo.original,
                showModal: true
            });
        }
    }
}

render() {
    return (
        <ReactTable
            data={tableData}
            filterable={true}
            getTrProps={this.onRowClick}
            columns={[
                {
                    Header: "Header",
                    accessor: "service_name"
                },
                ...
            ]}
        />
        {this.state.showModal &&  React.createPortal( <Modal data={this.state.data}>Your Data Goes Here</Modal>, document.getElementById('modal-portal')) }
    )
}

EDIT:

They have a Modal example in their Portal docs that you should check out.

EDIT 2:

this.state.showModal is a piece of state that you need to add. You will use this to conditionally render the <Modal /> component (that you create). What I've done here is shorthand for:

if(this.state.showModal) {
    return React.createPortal(...);
} else {
    return null;
}

As for the actual <Modal /> component, you can make that however you want, you can use a react modal package, bootstrap modals or just build your own.

Example custom Modal.js:

const Modal = ({ children, data }) => (
    <div className="my-modal">
        {children}
        // Here you can do stuff with data if you want
    </div>
);

CSS:

.my-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
Chase DeAnda
  • 15,963
  • 3
  • 30
  • 41
  • Thanks for taking the time to respond! So, I have a question about the last line of code. I'm a little confused as to what this.state.showModal is doing, but as for the second part, it appears this is how the actual Modal component gets mounted (via passing it into React.createPortal). Am I thinking about this correctly? I'm assuming you're passing it to the 'body' element? If I had an id of 'modal-root', is this what I would be passing in? **Edit: could you show me an example of what the modal component itself would look like? – Lukon Oct 16 '17 at 20:17
  • @Lukon Updated answer. Let me know if you have anymore questions! – Chase DeAnda Oct 16 '17 at 20:26
  • Thank you! So I gave it a shot and it appears that when I click on the row, no modal is being displayed and I'm not getting any errors from React. My onRowClick function is identical to yours, as is my Modal and css styles. The only thing that I modified was: {this.state.showModal && ReactDOM.createPortal( , document.getElementById('modal-portal')) }. Is this incorrect? I really appreciate your help btw! – Lukon Oct 16 '17 at 20:40
  • I also tried: const modalRoot = document.getElementById('modal-portal') and passing that into the createPortal function, but same issue. When I try 'body', I receive a 'body' is not defined error. – Lukon Oct 16 '17 at 20:47
  • Sorry, yes `body` won't work. You could try `document.body`, but creating a div with an id of `modal-portal` in your index.html page would be better. Then you'd pass that node as the last arg to `createPortal`. I was also just giving example code for the modal and not anything working. I'll update to working example. – Chase DeAnda Oct 16 '17 at 20:58
  • Okay update answer. Nothing was showing because we were rending `this.props.children` in Modal, but weren't passing anything. We are passing `data` as a prop, but I don't know what data gets returned when a user clicks on a row. Whatever you want to display in the modal, save in `this.state.data` and then you can use that in `` when they click a row. – Chase DeAnda Oct 16 '17 at 21:01
  • Awesome, thank you very much! I believe this will help me implement my own solution. You've been incredibly helpful and I really appreciate it! – Lukon Oct 16 '17 at 21:07
0

Note ReactDOM.createPortal its a function from react-dom not react

import {createPortal} from 'react-dom'

dorriz
  • 1,927
  • 3
  • 12
  • 19