markfinger / python-react

Server-side rendering of React components
MIT License
1.62k stars 116 forks source link

Design question (React-router, Redux) #51

Open Ppjet6 opened 9 years ago

Ppjet6 commented 9 years ago

Hi there,

I was wondering if you had any experience with react-router and redux together for server-side rendering. I know about HorizonXP/react-router-render, (which would need an update to react-router 1.x), but to use Redux I would need to wrap my component/routes into <Provider> (with the initial data that I must also provide somehow).

Is there any way we can make this work with python-react/react-render?

Related: https://github.com/rackt/react-router/blob/master/docs/guides/advanced/ServerRendering.md http://rackt.org/redux/docs/recipes/ServerRendering.html#async-state-fetching

TheoRyley commented 9 years ago

Python-react allows you to replace the default node server with your own. The redux server rendering examples use a server with a handleRender function that takes an express request. You could adapt these examples to use data passed from python-react instead of express. If you want to use redux and react-router together you should also check out https://github.com/rackt/redux-router and https://github.com/rackt/redux-router/pull/95. Look at what data the examples are getting from express and think about how you could pass it using python-react instead.

markfinger commented 9 years ago

Yeah, python-react's not much more than a pipeline to handle the use-case of rendering a react component with props from your python process's data. You can replace both the python and the node interfaces if you need to either pass data in a particular format, or access some particular functionality.

As @TheoRyley said, you'll probably need to replace the node server with something that matches your needs. If you need to also pass extra data from the python side (request headers, session data, etc), you can either define a renderer kwarg - or roll your own pipeline - to pass data in whatever format you need.

Hmmm, I think there are a bunch of people that have had to solve similar problems: @HorizonXP's react-router-render, for example. It would be pretty useful if we could pull all this stuff in to one place somewhere - either links to repos/packages or example code for more succinct stuff.

Ppjet6 commented 9 years ago

Redux-router seems nice indeed, I'll try to integrate it as well.

So if I understand correctly, I'd have to subclass the renderer, add one or more arguments, and rewrite the request to pass in my initial state. And on the express side I'd need to send back my state with the generated html, to include it in my template, (as shown in the redux example above).

Maybe we can try to make the render_component function a bit more generic and add in kwargs, that would be passed via RenderServer.render to the node server and added in turn to the RenderedComponent class. What do you think?

HorizonXP commented 9 years ago

So I've moved on from trying to get Django/Python to render my React apps. Instead, I use Django with Django REST Framework to serve my API, and then I have a Node.js server running that simply handles every request outside of /api/.

Combined with Redux-router, React, etc., it means that when a user visits a URL, they get a complete server-rendered page with data preloaded from the Django-based API. Then once the React components are loaded on the client-side, they navigate "locally" with data being pulled from the API, never touching the Node.js server again.

I went this way because it simplified how I developed the front-end application, since it's now completely React-based. It made little sense to have Django serve up a template just to pass off server-rendering to Node.js, and then come back.

So Redux & Redux-Router are fantastic, once you get them working correctly. With my new setup, Django does what it's best at, and Node does what it's best at, and the two services live separately.

In production, I run these in Docker containers, so it's even more modular.

Just my two cents. :-)

markfinger commented 9 years ago

@Ppjet6 yeah, the renderer's not the most useful thing to subclass. It'd be great to split it up so that the render method just chains a bunch of functions: handle_options, build_request_data, send_request, etc. PRs are always welcome!

@HorizonXP your setup sounds pretty cool. I've always wanted to try a setup with distinct front-end & back-end servers :) Does your Node server do much more than routing and pre-rendering? How do you handle auth (if it's relevant) in your setup?

HorizonXP commented 9 years ago

@markfinger Node.js handles mostly just routing and pre-rendering, not much more than that. Right now, I have it set up to do hot module reloading with Webpack, so that as I write React components, they're updated live in the browser, making development faster.

I do handle auth in my setup. I have it set up such that when a request is made, it takes the cookie and sends it onwards to the Django backend to verify its validity. If it's valid, the Django backend returns the serialized User object, which allows the Node.js front-end code to continue loading with the "login required" routes. If it's not valid, it returns an error, which Node.js interprets as a failed auth, and then forwards the user to the login page with the error populated.

It means that any request requires a minimum of 2 API calls, 1 for auth, and 1 for the actual request. However, it also means that the Node.js front-end is completely stateless. And since I'm using JSON Web Tokens, the Django backend is stateless as well, except for the SQL database for everything else.

markfinger commented 9 years ago

:open_mouth: oooooh, that's really cool :)

Ppjet6 commented 9 years ago

Thank you all for your comments :)

@HorizonXP, that was going to be my next question! I also have Django REST Framework, and when I thought about adding in server-rendering I first wanted to do it in python, thinking that maybe I could avoid some overhead, (and also I prefer python). But since I have to make a round-trip to the node server anyway, why not invert the pattern.

A few question if you allow me! Do you have some reverse proxy in front of both node and python (still requesting the api from node when needed)? Or are you making everything go through node directly? Any shortcomings you can think of maybe?

Sorry I'm getting a bit off-topic

jamesattard commented 7 years ago

@HorizonXP "then once the React components are loaded on the client-side, they navigate "locally" with data being pulled from the API, never touching the Node.js server again."

How do you make sure that the components are loaded, and a consequence, pointing to API and not Node?