简体   繁体   中英

How to do server-side rendering in React/redux?

I am new to react/redux I little confused with server side rending in react/redux, Yes i saw some example on the internet but when i tried with mock api with external server , server side rendering is not working .

cat.js

import React from 'react';
import {render} from 'react-dom';
import {connect} from 'react-redux';
import * as mockApi from '../Actions/mockActions';
class Cat extends React.Component{
    componentWillMount(){
        this.props.getMockApi();
    }
    render(){
        return(
            <div>
                Hello Dude
                {this.props.mock.data.map((data,i) => {
                    return <li key={i}>{data.email}</li>
                })}
            </div>
        )
    }
}

const mapStateToProps = (state) => {

    return {
       mock:state.mock
    }
};
const mapDispatchToProps = (dispatch) => {
    return {
        getMockApi:() => dispatch(mockApi.getMockData())
    }
};


export default connect(mapStateToProps,mapDispatchToProps)(Cat);

mockActions.js

import axios from 'axios';
import * as types from './actionTypes';

export function getMockData() {
    return dispatch => {
        return axios.get('http://jsonplaceholder.typicode.com/users').then(response => {
            dispatch(setThisData(response.data))
        })
    }
}

export function setThisData(data) {

    return {
        type:types.MOCK_API,
        payload:data
    }
}

App.js

import React from 'react';
import {render} from 'react-dom';

import Cat from './components/cat'
import {Provider} from 'react-redux';
import configureStore from './Store/configureStore';
import { createStore ,applyMiddleware,compose} from 'redux';
import counterApp from './Reducers'
import thunk from 'redux-thunk';







if(typeof window !== 'undefined'){
    // Grab the state from a global variable injected into the server-generated HTML
    const preloadedState = window.__PRELOADED_STATE__

// Allow the passed state to be garbage-collected
    delete window.__PRELOADED_STATE__
    const store = createStore(counterApp, preloadedState, compose(applyMiddleware(thunk)))


    render(
        <Provider store={store} >
            <Cat/>
        </Provider>
        ,
        document.getElementById('app')
    )



}

devServer.js

import express from 'express';
import path from 'path';
import webpack from 'webpack';
import webpackMiddleware from 'webpack-dev-middleware'
import webpackHotMidleware from 'webpack-hot-middleware';
import bodyParser from 'body-parser';
import React from 'react'
import { createStore } from 'redux'
import { Provider } from 'react-redux';
import counterApp from '../../src/client/ReduxServer/Reducers';
import App from '../../src/client/ReduxServer/components/cat';
import { renderToString } from 'react-dom/server'


import webpackConfig from '../../webpack.config.dev';


let app = express();
app.use(bodyParser.json());
app.use(express.static('public'))


const compiler = webpack(webpackConfig);

app.use(webpackMiddleware(compiler, {
    hot: true,
    publicPath: webpackConfig.output.publicPath,
    noInfo: true
}));

app.use(webpackHotMidleware(compiler));


// app.get('/*', (req, res) => {
//     res.sendFile(path.join(__dirname, '../../index.html'))
// });


//Redux Start
app.use(handleRender);

function handleRender(req,res) {
   const store = createStore(counterApp);

   const html = renderToString(
       <Provider store={store} >
           <App/>
       </Provider>
   )
    const preloadedState = store.getState();
    // Send the rendered page back to the client
    res.send(renderFullPage(html, preloadedState))
}
function renderFullPage(html, preloadedState) {
    console.log(preloadedState)
    return `
            <!doctype html>
    <html>
      <head>
        <title>Redux Universal Example</title>
      </head>
      <body>
         <div id="app">${html}</div>
          <script>
           window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
        </script>

      <script src="bundle.js"></script>

      </body>
  </html>
        `
}







//Redux Ends








app.listen(3000, () => {
    console.log('Listening')
});

Right now this will only server render the hello dude but not the mock Api call data .I know that missed to fetch the data from server side but the point is what will i do If ihave to render a two components and that component has 5 api reuqest ,And how to fecth the correct api Request

Right Now My client Side Prefecthed state will look like this

 window.__PRELOADED_STATE__ = {"mock":{"data":[]}}

Ok, to make this clear, you've created the code to handle server rendering. However, it doesn't load the data that is supposed to be fetched right?

You've done the first step, great! The next step is to load the actual dynamic data to the store. Let's look at this code here

 function handleRender(req,res) { const store = createStore(counterApp); const html = renderToString( <Provider store={store} > <App/> </Provider> ) const preloadedState = store.getState(); // Send the rendered page back to the client res.send(renderFullPage(html, preloadedState)) } 

What happened is that you created a store. The store is used to render the html into a string. Then you get the store state and put it into preloadedState.

This is great accept that renderToString will not call this.props.getMockApi(); as you would expect.

Instead, you have to fetch the state before you call renderToString();

In this case, what you could do is as following. (Note that this is just an example, you probably want to use something more general in production, especially if you use something like react-router.)

 import * as mockApi from '../Actions/mockActions'; function handleRender(req, res) { const store = createStore(counterApp); store.dispatch(mockApi.getMockData()) // And since you used redux-thunk, it should return a promise .then(() => { const html = renderToString( <Provider store={store}> <App/> </Provider> ) const preloadedState = store.getState(); // Send the rendered page back to the client res.send(renderFullPage(html, preloadedState)) }); } 

Simple isn't it? ;D, nah just joking. This is one part of react where there's not really an exact solution to the problem yet.

Personally, if I had the choice to go back in time, I'd tell myself to learn other stuff other than server rendering. There are other techniques such as code splitting, lazy loading, etc that I could've used instead. With server rendering, if the javascript arrives long after the user has seen the initial page, they might get frustrated by other things that require js. For example in my case, some links are not working, some buttons don't do anything, etc.

I'm not saying that server rendering is not good. It's an interesting technique, just that there are other techniques that are more beneficial to learn first (Oh, and server rendering basically locks you to use nodejs for your backend). Good luck to you :)

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