1

I am currently working on a new project, so I decided to implement React but with server side rendering. I use express as router between pages, so when you access to the home page, the entry point is something like this:

const router = require('express').Router();
const { render, fetchUsers } = require('./controller');

router.use('/', fetchUsers, render);

module.exports = router;

So when you acces the home page, this would get all the users and then it will render the component, in order to render the component I do the following:

const render = (req, res) => {
    const extraProps = {
        users: res.locals.users.data,
    }
    return renderView(View, extraProps)(req, res);
}

the fetchUsers method sets res.locals.users with an api response. My renderView do something like this:

const renderView = (Component, props = {}) => (req, res) => {
const content = renderToString(
    <LayoutWrapper state={props}>
        <Component {...props} />
    </LayoutWrapper>

);
res.send(content);

};

My LayoutWrapper is a React Component that replace the html template:

const React = require('React');
const serialize = require('serialize-javascript');

const LayoutWrapper = ({ children, state }) => (
    <html>
        <head></head>
        <body>
            <div id={'app-root'}>
                {children}
            </div>
        </body>
        <script>
            {`window.INITIAL_STATE = ${serialize(state, { isJSON: true })}`}
        </script>
        <script src={`home.js`} />
    </html>
)


module.exports = LayoutWrapper;

The script that sets window.INITAL_STATE = props; is used on the client side to get the props that were fetch. But the problem is the way that the renderToString process the component. The console.log output is the following:

<html data-reactroot="">

<head></head>

<body>
  <div id="app-root">
    <div>I&#x27;m the Home component</div><button>Press me!</button>
    <ul>
      <li>Leanne Graham</li>
      <li>Ervin Howell</li>
      <li>Clementine Bauch</li>
      <li>Patricia Lebsack</li>
      <li>Chelsey Dietrich</li>
    </ul>
  </div>
</body>
<script>
  window.INITIAL_STATE = { & quot;users & quot;: [{ & quot;id & quot;: 1,
      & quot;name & quot;: & quot;Leanne Graham & quot;
    }, { & quot;id & quot;: 2,
      & quot;name & quot;: & quot;Ervin Howell & quot;
    }, { & quot;id & quot;: 3,
      & quot;name & quot;: & quot;Clementine Bauch & quot;
    }, { & quot;id & quot;: 4,
      & quot;name & quot;: & quot;Patricia
      Lebsack & quot;
    }, { & quot;id & quot;: 5,
      & quot;name & quot;: & quot;Chelsey Dietrich & quot;
    }]
  }
</script>
<script src="home.js"></script>

</html>

Is there any way to do this without having to declare the html template as a simple string, and instead having a Wrapper component that sets the html code structure?

2 Answers2

0

You can replace all escaping symbols, but only after renderToString calling.

renderToString(...).replace('& quot;', '')

Security notes.

avdotion
  • 105
  • 8
0

To escape html characters in renderToString method (like &quot;), use dangerouslySetInnerHTML:

Correct

const scriptStr = window.INITIAL_STATE = {a: 'a'};

return renderToString(
    <script dangerouslySetInnerHTML={scriptStr} />
);

It makes:

"<script>window.INITIAL_STATE = {a: \"a\"}</script>"

BAD:

const scriptStr = window.INITIAL_STATE = {a: 'a'};

return renderToString(
    <script>
        {scriptStr}
    </script>
);

It makes:

"<script>window.INITIAL_STATE = {a: &quot;a&quot;}</script>"
Ivanes
  • 515
  • 2
  • 14