Monday, 18 February 2019

react-helmet mixing fields during renderStatic

I'm running server-side render of the React application. I'm using express for this purposes. The whole server-side render code looks like this:

import * as React from "react"
import * as ReactDOMServer from "react-dom/server"
import * as express from "express"
import { StaticRouter } from "react-router-dom"
import walker = require("react-tree-walker")
import { useStaticRendering } from "mobx-react"

import Helmet from "react-helmet"
import Provider from "../src/Provider"
import { StaticRouterContext } from "react-router"

import version = require("../version")

interface IRenderResponse {
    statusCode: number,
    template: string,
    redirect?: string
}

const run = (url: string, locale?: string): Promise<IRenderResponse> => {
    var template: string = require(`../dist/${version.v()}/index.html`)
    var html: string = ""
    var head: object
    var context: StaticRouterContext = {}

    useStaticRendering(true)

    var routing = (
        <StaticRouter 
            location={url} 
            context={context}
        >
            <Provider defaultLocale={locale} />
        </StaticRouter>
    )

    return new Promise((resolve) => {
        walker(routing, (element, instance) => {
            if (instance && typeof instance._prepare == typeof (() => {}))
                return instance._prepare()
        }).then(() => {
            html = ReactDOMServer.renderToString(routing)
            head = Helmet.renderStatic()
            template = template.replace(/\${__rh-([a-z]+)}/gi, (match, group) => {
                return head[group].toString()
            })
            template = template.replace(/\${__body}/gi, (match) => {
                return html
            })
            if (context.url)
                context["statusCode"] = 301

            resolve({
                statusCode: context["statusCode"] || 200, 
                template, 
                redirect: context.url
            })
        }).catch((error) => {
            template = template.replace(/\${__rh-([a-z]+)}/gi, "")
            template = template.replace(/\${__body}/gi, error.stack || error.toString())
            resolve({
                statusCode: 500, 
                template
            })
        })
    })
}

var app = express()

app.get("*", (req, res) => {
    var accepted = req.acceptsLanguages()
    var locale = accepted ? (accepted[0] || "ru").split("-")[0] : "ru"
    run(req.originalUrl, locale).then((data) => {
        if (data.redirect)
            res.redirect(data.redirect)
        else
            res.status(data.statusCode).send(data.template)
    })
})

app.listen(1239)

You can see that the react-tree-walker is used here. But this problem occurs whatever I'm using for the server-side render.

The problem is that if my node-js server is running in one thread, the if two different requests are being done simultaneously, then react-helmet mixes fields. For example, if there are two views:

class FirstView extends React.Component {
    render() {
        return (
            <Helmet>
                <title>This is first title</title>
                <meta name="description" content="My first view description" />
            </Helmet>
        )
    }
}

and

class SecondView extends React.Component {
    render() {
        return (
            <Helmet>
                <title>This is second title</title>
                <meta name="description" content="My second view description" />
            </Helmet>
        )
    }
}

then I can receive the head something like that:

<title>This is first title</title>
<meta name="description" content="My second view description" />

Obviously this happens because of react-helmet uses static fields, I suppose. So, if two request are being handled in parallel, this fields are being changed chaoticaly.

How can I defeat it? This problem often occurs for high-load projects, and this can crash the SEO, because crawler can receive wrong data.



from react-helmet mixing fields during renderStatic

No comments:

Post a Comment