简体   繁体   中英

React isomorphic component with both client-side and server-side rendering

I want to create a react app with both client side and server side rendering.

Here is the example:

import styles from './Main.css';

import React, {Component} from 'react';
import Info from './Info/Info';
import Record from './Record/Record'

export default class Main extends Component {
    render() {
        return (
            <div className={styles.main}>
                <div className={styles.mainIn + ' clearfix'}>
                    <div className={styles.mainLeft}>
                        <Info info_num="2012201972"/>
                    </div>
                    <div className={styles.mainRight}>
                        <div className="clearfix mb20">
                            <Record />
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}

In this component Main , it needs to be rendered at the client side except <Record />

Component Record

import styles from './Record.css';
import layout from '../../shared/styles/layout.css'

import React, {Component} from 'react';

export default class Record extends Component {
    render() {
        return (
            <div className="float_two">
                <div className={layout.box + ' mr10'}>
                    This is Record!
                </div>
            <div>
        )
    }
}

Here is my question:

I have searched some examples of server-side rendering examples with ReactDom.renderToString and react-router . However, there is no tutorial with both client-side and server-side rendering.

What I want to achieve is that, client first loads and renders Component <Main /> and then loads <Record /> from server-side.

Another question is that, how to load the style module Record.css with renderToString, because I think in this renderToString can just load the html things not the css.

When people refer to server-side rendering they are usually referring to the initial rendering of the top-level application at a certain route, not individual components.

I'm having trouble understanding your use case is for what you request. Your React application is one big tree of Fragments , so rendering a single component server-side does not really make sense. If you want Record to be a part of React then the client will need to know about it, so why not just render it on the client side as per usual?

If you really need to render it server side then I guess you could build the Record component so that it does an AJAX request and then the html returned could be rendered using https://facebook.github.io/react/tips/dangerously-set-inner-html.html , but I wouldn't recommend it.

My guess is that Record requires some kind of data from the server side and that is why you want to render it there? Instead just fetch that data as JSON and use that to render the component client side.


Having read your comments, I know what you are trying to do. What you want is to dynamically load content (not rendered html) from the server in response to some event (scrolldown, button click or whatever). React is very good at this. By changing the state (ie what Records there are) of your application, React will take care of rerendering efficiently.

This is a very simple application. It starts by having 2 items (foo and bar) that should be rendered. In response to an action (button click in this case) more data is loaded into the state and thus rendered to the page. All you need to do is modify this so that instead of the setTimeout you do an AJAX call to your backend to get the actual data.

Live version here: https://codepen.io/dpwrussell/pen/qadrko

class Application extends React.Component {

  constructor(props) {
    super(props);

    // Start with 2 records
    this.state = {
      records: [
        {
          name: 'foo',
          description: 'Some foo'
        },
        {
          name: 'bar',
          description: 'Some bar'
        }
      ]
    };

    // Bind handlers
    this.loadMoreRecords = this.loadMoreRecords.bind(this);
  }

  // Method to call which gets more records on demand
  // Here I just use setTimeout and some static data, but in your case
  // this would be AJAX to get the data from your server where the callback
  // would do the setState. I use a 2 second delay to exaggerate a delay getting
  // the data from the server.
  loadMoreRecords() {
    setTimeout(() => {
      this.setState({
        records: this.state.records.concat([
          {
            name: 'hello',
            description: 'Some newly loaded hello'
          },
          {
            name: 'world',
            description: 'Some newly loaded world'
          }
        ])
      })
    }, 2000);
  }

  // Method to render whatever records are currently in the state
  renderRecords() {
    const { records } = this.state;
    return records.map(record => {
      return (
        <li>{ `${record.name} - ${record.description}` }</li>
      );
    })
  }

  // React's render method
  render() {
    return (
      <div>
        <h1>List of Records Page</h1>
        <ul>
          { this.renderRecords() }
        </ul>
        <input type='button' onClick={this.loadMoreRecords} value='Load more Records' />
      </div>
    );
  }
}

/*
 * Render the above component into the div#app
 */
ReactDOM.render(<Application />, document.getElementById('app'));

Using css-modules-require-hook . It's similar to babel-register but for .css file. Basically, it transforms your require('Record.css') to a javascript Object base on your hook config. So your hook config should be same as your webpack css-loader config.

Put it in the entry file of your server.

const hook = require('css-modules-require-hook');

hook({/* config */});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM