0

I've watched,

http://youtu.be/z5e7kWSHWTg?t=15m17s

and read,

https://github.com/ryanflorence/react-training/blob/gh-pages/lessons/05-wrapping-dom-libs.md

https://github.com/ryanflorence/react-training/tree/gh-pages/code/Dialog

http://jsbin.com/dutuqimawo/edit?js,output

How to create a React Modal(which is append to `<body>`) with transitions?

and I get the concept of the Portal, that you're tricking React into ceasing its rendering for one piece of the DOM, then continuing the rendering afterward, so you can tinker with that piece of the DOM without confusing React by making its virtual DOM get out of sync.

My problem is that the examples all address a Dialog that is rendered at the end of the page, but appears inline when you're reviewing your code. It's a cool trick for using a jQuery modal, but I need a jQuery datepicker whose div actually remains where I put it. (As an aside, I'm also curious about GetDOMNode's presence in the examples when it's deprecated? I suppose you use FindDOMNode, although you call it slightly differently, plus the documentation says "In most cases, use of this escape hatch is discouraged because it pierces the component abstraction", which makes me a little gunshy to use it.)

To isolate the jQuery datepicker from React, I originally created one React component to handle everything above the datepicker, and another to handle everything below the datepicker, and used event listeners in each component to listen for updates. However, I prefer the design of a single parent component that passes everything down to its children; it seems like a cleaner design.

I redesigned it with a single parent and it seems to work, but I have no idea if my portal is really isolated from React's virtual DOM or not; it's my first crack at a portal so I'm really muddling through. (I am using React-Bootstrap for my navbar and it works great; I just couldn't find an equivalent to jQuery's datepicker and I like how it looks and operates, which is why I'm sticking with it.)

Here's my top-level component (I removed the props/componentDidMount/etc for clarity). The <CalendarControl /> is going to be the portal:

var ReactApp = React.createClass({

    render: function() {
        return (
            <div>
                <BootstrapNavbar division={this.state.division} dps={this.state.dps} sections={this.state.sections} />

                <div className="container">
                    <br />
                    <br />
                    <br />

                    <div className="row">
                        <div className="col-md-4" id="calendarPortal">
                            <CalendarControl />
                        </div>
                        <div className="col-md-8">
                            <h3>{this.state.dp}</h3>
                            <h4>{this.state.dpStartDate} - {this.state.dpEndDate}</h4>
                        </div>
                    </div>

                    <TimebookTableRecords timebookRecords={this.state.timebookRecs} />
                </div>
            </div>
        );
    }
});

Here's the code for my CalendarControl portal. When the CalendarControl mounts, I'm creating a new div calendarControl as a child of calendarPortal. I then use jQuery to create the datepicker on the calendarControl div.

var CalendarControl = React.createClass({
    render: function () {
        return null;
    },
    componentDidMount() {
        var portalLocation = document.getElementById("calendarPortal");
        var newElement = document.createElement('div');
        newElement.id = "calendarControl";
        portalLocation.appendChild(newElement);
    },

    componentWillUnmount() {
        var portalLocation = document.getElementById("calendarPortal");
        document.body.removeChild(portalLocation);
    },
});

Here's the jQuery code that creates a datepicker on the calendarControl div:

$("#calendarControl").datepicker({
   numberOfMonths: monthDiff,
   defaultDate: dpStartDate,
   showButtonPanel: false, 
   beforeShowDay: formatCalendarDays, //formatter function
   onSelect: dateClicked //handles click on calendar date

The final product seems to work fine, and doesn't generate any "the DOM was unexpectedly mutated" errors like when you manipulate part of the DOM that's under React's purview. I can update the state of the parent and see the changes propagate down nicely, and use jQuery to update the calendar.

enter image description here

However, I just don't know if this is the correct approach? That is to say, have I achieved a true portal here? I used the Google Chrome React Developer Tools add-in to inspect the component hierarchy, and it does look like from React's perspective there's a null in the CalendarControl div:

enter image description here

Thanks for bearing with me in this lengthy post. I have to say that so far I'm really loving the React approach to web development; it's so radically different that it took a number of readings and tinkering just to understand its concepts, but now it seems so much more elegant than the ways I've done it in the past.

Community
  • 1
  • 1
James Toomey
  • 5,635
  • 3
  • 37
  • 41

1 Answers1

0

From my understanding of portals, you are doing this mostly correct. But if it had any other children, you would have to reconnect with react after the jquery stuff, but I assume that is not the case here.

The reason you are seeing a "null" inside calendar control is because you return a null in your CalendarControl render function.

Why don't you just change your render function in calendarControl to:

render: function () {
   return (
         <div id="calendarControl"></div>
)

and do all your funky jQuery rendering inside componentDidMount function?

Eivind
  • 18
  • 1
  • 3
  • That seems like a better approach, especially by having the jQuery inside componentDidMount, because the code is all in one place and it's more clear what is happening. I also like the idea of rendering the calendarControl div in the component because it eliminates the need for the calendarPortal div. Thanks for answering on this one--I just got the dubious "Tumbleweed" badge for posting a question with no answers and low views. It was funny and embarrassing at the same time. – James Toomey Apr 04 '16 at 16:41