onClick handler not registering with ReactDOMServer.renderToString

10,682

Solution 1

React DOM render to string only sends the initial HTML as a string without any JS.
You need a client side react router as well which will attach the required JS handlers to the HTML based on their react-id's. The JS needs to run on both sides.
Universal rendering boilerplate for quick start. https://github.com/erikras/react-redux-universal-hot-example
Another question which is similar to yours. React.js Serverside rendering and Event Handlers

Solution 2

None of the hooks will register with ReactDOMServer.RenderToString. If you want to accomplish server side rendering + hooks on your react component, you could bundle it on the client (webpack, gulp, whatever), and then also use ReactDOMServer.RenderToString on the server.

Here's a blog post that helped me accomplish this: https://www.terlici.com/2015/03/18/fast-react-loading-server-rendering.html

Share:
10,682
Alexander Mills
Author by

Alexander Mills

Dev, Devops, soccer coach. https://www.github.com/oresoftware

Updated on June 06, 2022

Comments

  • Alexander Mills
    Alexander Mills almost 2 years

    I am trying to copy this fiddle: http://jsfiddle.net/jhudson8/135oo6f8/

    (I also tried this example http://codepen.io/adamaoc/pen/wBGGQv and the same onClick handler problem exists)

    and make the fiddle work for server side rendering, using ReactDOMServer.renderToString

    I have this call:

       res.send(ReactDOMServer.renderToString((
                <html>
                <head>
    
                    <link href={'/styles/style-accordion.css'} rel={'stylesheet'} type={'text/css'}></link>
    
                </head>
    
                <body>
    
    
                <Accordion selected='2'>
                    <AccordionSection title='Section 1' id='1'>
                        Section 1 content
                    </AccordionSection>
                    <AccordionSection title='Section 2' id='2'>
                        Section 2 content
                    </AccordionSection>
                    <AccordionSection title='Section 3' id='3'>
                        Section 3 content
                    </AccordionSection>
                </Accordion>
                </body>
                </html>
            )));
    

    the Accordion element looks like so:

    const React = require('react');
    
    const fs = require('fs');
    const path = require('path');
    
    
    const Accordion = React.createClass({
    
        getInitialState: function () {
            // we should also listen for property changes and reset the state
            // but we aren't for this demo
            return {
                // initialize state with the selected section if provided
                selected: this.props.selected
            };
        },
    
        render: function () {
    
            // enhance the section contents so we can track clicks and show sections
            const children = React.Children.map(this.props.children, this.enhanceSection);
    
            return (
                <div className='accordion'>
                    {children}
                </div>
            );
        },
    
        // return a cloned Section object with click tracking and 'active' awareness
        enhanceSection: function (child) {
    
            const selectedId = this.state.selected;
            const id = child.props.id;
    
            return React.cloneElement(child, {
                key: id,
                // private attributes/methods that the Section component works with
                _selected: id === selectedId,
                _onSelect: this.onSelect
            });
        },
    
        // when this section is selected, inform the parent Accordion component
        onSelect: function (id) {
            this.setState({selected: id});
        }
    });
    
    
    module.exports = Accordion;
    

    and the AccordionSection component looks like so:

    const React = require('react');
    
    
    const AccordionSection = React.createClass({
    
        render: function () {
    
            const className = 'accordion-section' + (this.props._selected ? ' selected' : '');
    
            return (
                <div className={className}>
                    <h3 onClick={this.onSelect}>
                        {this.props.title}
                    </h3>
                    <div className='body'>
                        {this.props.children}
                    </div>
                </div>
            );
        },
    
        onSelect: function (e) {
            console.log('event:',e);
            // tell the parent Accordion component that this section was selected
            this.props._onSelect(this.props.id);
        }
    });
    
    
    
    module.exports = AccordionSection;
    

    everything works, and the CSS is working, but the problem is that the onClick doesn't get registered. So clicking on the accordion elements does nothing. Does anyone know why the onClick handler might not get registered in this situation?

    • Jeff P Chacko
      Jeff P Chacko about 8 years
      is JS working at all? is any of the JS working on client?
    • Alexander Mills
      Alexander Mills about 8 years
      I think the missing piece is that on the client once the markup gets there we need to make some more React calls that actually bind the handlers