ReactJS server side rendering fo single page application
Asked Answered
L

1

8

What I've already done

  • I have a frontend entirely built with React.JS.
  • All the business logic is handled by Django and exposed by Django REST Framework via an API.
  • I'm able to build for different mobile environnements (Android and iOS via Cordova)
  • The web application is accessible via my Django project (the same that exposes the api), the frontend is sill the same ReactJS code bundled via webpack.
  • The App has a single entry point, main.js which is a bundled version of my react.js components and dependencies, so my index.html typically looks like this :

    <body>
        <script type="text/javascript" src="/static/bundles/main-3997ad3476694c3c91cf.js"></script>
    </body>
    

What I want to do

  • I want to provide a server-side rendering of my web application to let web crawlers correctly index my app on web (I'm not looking for server-side rendering for mobile builds)

How can I handle this considering the fact that my app is a Single Page Application ? I do not want to reinvent the wheel nor to duplicate my code. What kind of node.js server do I have to write in order to achieve this automatic server-side rendering ? Is there any way to provide the server side rendering directly in Django (via some tools reading and interpreting the final results of the page as displayed on the client-side and returning this raw html ?)

Linnea answered 30/7, 2015 at 15:55 Comment(6)
Normally you would set up a Node.js backend, and use React.renderToString. Because you are using Django, though, you may have to use something like this github.com/markfinger/python-reactAlbertalberta
I've already looked to this package but I can't find a way to make it work...I'm struggling with the "POST" parameters not accepted by the server, and I do not know how to configure the "reverse proxy" that he's talking...Linnea
What about this library? github.com/defrex/django-react I am not familiar with Django unfortunately to give actual help, but I would think you just want to get access to React's renderToString method during the server rendering portion on Django. Is there a way you can run server side JS in Django?Albertalberta
Looks promising ! I didn't see this Django fork, thanks :)Linnea
Take a look at reactstarterkit.com - it's a great starter project for a isomorphic javascript app. I'm running a django rest framework backend with a nodejs frontend and it's working out very nicely.Pellitory
It looks interesting but I don't wan't to duplicate my business logic with Flux, I just want ReactJS to be "pure" frontend, no business logic at all. How do you handle that ?Linnea
F
3

You have probably solved your problem by now, but I wanted to share my solution for this.

I have a very similar setup, and have something that seems to work pretty well so far. I basically have a django w/ DRF backend api and isomorphic React/Flux javascript app. I also run a node server next to the python backend server, which acts only as a 'template rendering' service. In essence, replacing the django render function.

So I simply replace the django View with a special IsoView which calls off via http to the node server and returns the rendered html.

from rest_framework.renderers import JSONRenderer
import requests

class IsoView(View):

    def user_json(self):
        if self.request.user.is_anonymous():
            return {'anonymous': True}
        else:
            return UserSerializer(self.request.user, context={'request': self.request}).data

    @classmethod
    def render(cls, request, template, data):
        req_data = JSONRenderer().render(data)
        try:
            headers = {'content-type': 'application/json'}
            query_params = request.GET
            resp = requests.post(_build_url(request.path), params=query_params, data=req_data, headers=headers, timeout=0.1)
            reply = resp.json()

            if resp.status_code == 302:
                return redirect(reply.get('redirect'))

            if 'error' in reply:
                raise Exception("\n\nRemote traceback: {}".format(reply.get('traceback')))

        except requests.exceptions.RequestException as err:
            logger.warn('IsoView request exception: {}'.format(err))
            reply = {}

        return render(request, template, {
            'react': reply.get('result'),
            'data': data
        })

And use it like so:

class HomePage(IsoView):

    def get(self, request, *args, **kwargs):
        return self.render(request, 'app.html', {
            'user': json_data...
        })

This also assumes a django template which uses something like this

<html>
<head>
    <script>
        window.data = {{ data|json }};
    </script>
</head>
<body>{{ react|safe }}</body>
</html>

What this does is it renders the html returned from node in the body tag and also dumps the json data required for bootstrapping the app on the client in the window.data object.

This is a really simplified version of the system, but it should work. You should be careful with XSS attacks on the window.data bit, so make sure to escape all your json data but other than that, you should be all good.

Then the node template server looks really similar to any of the tutorials online that you can find for server-side react. Just a simple express app.

Alternatively, you don't need to mess around with django templates at all if you render the full ... in node and return that as a string.

Hope that helps.

Fasten answered 6/10, 2015 at 12:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.