appbaseio / reactivesearch

Search UI components for React and Vue
https://opensource.appbase.io/reactivesearch
Apache License 2.0
4.9k stars 466 forks source link

Add SSR support for reactivesearch #239

Closed mrtinkz closed 6 years ago

mrtinkz commented 6 years ago

Issue Type:

Reactive Base component have server side rendering issues, it tries to uses the window which will not be available on the server. Description:

I have been giving it a try and found that the Reactive Base component does have a server side rendering issues, it tries to uses the window which will not be available on the server. Not sure if other components have similar issues, but it would be great if all of the components have SSR support. I got it working by rendering it only on the client side but that's a bad practice and is not recommended.

Screenshots:

Minimal reproduction of the problem with instructions:

Reactivesearch version: 2.2.0

Browser: [all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]

Anything else:

aol-nnov commented 6 years ago

@mrtinkz, while experimenting with react-universally and reactivesearch I've added the following snippet into my SSR middleware and had no more issues so far. YMMV though...

if(typeof window === 'undefined') {
    global.window = {
      location: {
        search: request.query
      }
    };
  }
metagrover commented 6 years ago

Thanks! We will implement a solid fix for this in the coming release, which will allow you to inject request.query via props to the ReactiveBase component, thereby conditionally picking query-params from the props if window is not present in the scope.

metagrover commented 6 years ago

@aol-nnov @mrtinkz Besides window issue, are you guys seeing any issue with the default styling? We use emotion for component scoped styling. Is it working for you guys properly? It'd be great if you can share a code snippet with us.

aol-nnov commented 6 years ago

@metagrover, my experiments were very limited, unfortunately.

What I've done so far is a simple quckstart app similar to your examples, but with ssr. And the window issue was the only stopper for me.

I'll create a separate issue if I find anything down the road. I'm very limited with spare time now, sorry..

aol-nnov commented 6 years ago

@metagrover indeed, there are issues with emotion on server side:

ReferenceError: document is not defined
    at makeStyleTag (/Users/aol/react/react-universally/node_modules/emotion/src/sheet.js:39:22)
    at StyleSheet.inject (/Users/aol/react/react-universally/node_modules/emotion/src/sheet.js:59:22)
    at Object.<anonymous> (/Users/aol/react/react-universally/node_modules/emotion/src/index.js:15:7)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.require (module.js:497:17)
    at require (internal/module.js:20:19)
divyanshu013 commented 6 years ago

I think we can follow the steps in https://emotion.sh/docs/ssr to resolve this

metagrover commented 6 years ago

Have you added any external styles (from emotion) in your app? How come you didn't see these errors before when you ran the app using:

if(typeof window === 'undefined') {
    global.window = {
      location: {
        search: request.query
      }
    };
}

The issue I'm seeing with emotion is that the styles don't get applied to Reactivesearch components on the initial render via SSR without any errors.

aol-nnov commented 6 years ago

@metagrover no extra styles, only from reactivesearch

How come you didn't see these errors before

simple! I didn't read logs )))

As I told you, I barely have 30 minutes a day for this experiments now..

metagrover commented 6 years ago

Thanks! I will look into it.

mrtinkz commented 6 years ago

@metagrover @divyanshu013 I did not use emotion, so the windowwas the only problem I had. Sorry for the late reply.

aol-nnov commented 6 years ago

@metagrover seems I'm stuck with ssr.. Could you please give me some glue?

just curl-ed my page that was supposed to perform ssr and got

<div class="leftSidebar " data-reactid="32">
  <!-- react-empty: 33 -->
  <!-- react-empty: 34 -->
  <!-- react-empty: 35 -->
  <!-- react-empty: 36 -->
  <!-- react-empty: 37 -->
</div>

instead of my SingleList-s, MultiList-s and DynamicRangeSlider, which makes me think, that no data is present in props. Despite that I can see requests to ES server in logs (I've enabled them with the following command)

curl -XPUT 'http://<ip>:9200/<my_index>/_settings?preserve_existing=true' -d '{
  "index.indexing.slowlog.threshold.index.debug" : "0s",
  "index.search.slowlog.threshold.fetch.debug" : "0s",
  "index.search.slowlog.threshold.query.debug" : "0s"
}' -H "content-type: application/json"

As I already told you, I'm using react universally (redux-opinionated branch) as my ssr boilerplate.

Also, if I navigate to that page via js-enabled browser, contents is rendered just fine (but it's client-side rendering)

Any hints would be highly appreciated!

metagrover commented 6 years ago

@aol-nnov Thanks for the detailed info. I will need some time to figure out the solution for this.

(Heads up: I have very little experience with SSR so this could be totally wrong):

A possible way to get this to work:

Try passing the data from the ES query as props to the SingleList component and see if it gets rendered then? SingleList returns null when there is no data which is apparently happening here. Refer this prop here to pass the data to the SingleList component.

I'm not too sure if that will work though, since we check for options prop changes in componentWillReceiveProps. So in order to try this out properly, I suggest you set the options prop with a delay, so that the updates are handled by the component.

If it works in any of the above case, we will wire up the things in the library to support this properly. If you have any better solution in mind that can work, we are open to your suggestions.

aol-nnov commented 6 years ago

@metagrover thank you for responce!

Today I've stumbled upon profound SO answer on react lifecycle callbacks, and at first sight, it could be fixed by changing componentWillReceiveProps to componentWillUpdate since

componentWillReceiveProps(nextProps,nextState)
This function is called every time props are received except the first render

And that first render happens on server.

Also, as per that answer:

Does the change in state/props requires to data to be updated?
Example, parent container which formats data received after api call and passes the formatted data to children.
componentWillUpdate

I'll try that later and report back.

metagrover commented 6 years ago

Hey @aol-nnov, we use componentWillReceiveProps to selectively handle several types of prop updates accordingly. We can support the initial data rendering via constructor here and not componentWillUpdate, since it gets called on every state and prop changes which is not what we want here.

So, if you're at it, try it with constructor or componentDidMount method.

aol-nnov commented 6 years ago

@metagrover will do!

Thank you for the hints!

aol-nnov commented 6 years ago

@metagrover it seems, that componentDidMount is not fired on server side, so, I've added following snippet to the bottom of constructor:

this.updateQueryOptions(this.props);
this.updateQuery(Object.keys(this.state.currentValue), this.props);

also I've added some logging to mapStatetoProps and figured out that on server side it is fired only twice and state seems to be empty:

{ components: [],
  watchMan: {},
  queryList: {},
  queryOptions: {},
  dependencyTree: {},
  appbaseRef: 
   AppbaseClient {
     url: '192.168.248.4:9200',
     protocol: 'http',
     credentials: null,
     appname: 'myapp',
     headers: {} },
  config: 
   { url: 'http://192.168.248.4:9200',
     app: 'myapp',
     credentials: null,
     type: '*' },
  hits: {},
  aggregations: {},
  queryLog: {},
  selectedValues: {},
  isLoading: {},
  stream: {},
  streamHits: {},
  timestamp: {},
  headers: {} }

on the client side there are a hell more calls to it... so, no luck as of now...

aol-nnov commented 6 years ago

This blows my mind, but it looks like a key to success. ))

What do you think, @metagrover ?

metagrover commented 6 years ago

I was aware of the fact that the store isn't getting updated 😓. The medium post looks interesting though. Need to check up on redux store management with SSR. Will get to this shortly. Thanks!

aol-nnov commented 6 years ago

Hello! Any news on SSR?

I'd be really glad to know if there is any progress...

metagrover commented 6 years ago

Hello everyone,

Primary support for SSR has been added. We should be able to wrap it up in this week. You can check the progress on the ssr branch.

Cheers!

aol-nnov commented 6 years ago

@metagrover you are my hero! :-D

divyanshu013 commented 6 years ago

Mine too ❤️

mrtinkz commented 6 years ago

Fantastic , thank you....

metagrover commented 6 years ago

SSR support is out in v2.5.0 🎉

Setup process in covered in docs here.