Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

server-side rendering and sagas issue (originally "React Performance Measurement Error") #21

Closed
mxmtsk opened this issue Jul 19, 2016 · 16 comments
Labels

Comments

@mxmtsk
Copy link

mxmtsk commented Jul 19, 2016

Hi,

I've tried to implement redux-crud-store following the ReadMe file. As soon as I call my route in the browers I'll get this messages:

Warning: There is an internal error in the React performance measurement code. Did not expect componentDidMount timer to start while render timer is still in progress for another instance.

Uncaught TypeError: Cannot read property 'getIn' of undefined

Uncaught TypeError: Cannot read property '__reactInternalInstance$w5v7h28v50n414er620uow29' of null

I can't get my head around where the error lies but it does get fired when I call fetchCollection

Landingpage.js

import React, { Component, PropTypes } from 'react';
import styles from './Landingpage.scss'; // eslint-disable-line
import { connect } from 'react-redux';
import { fetchCities } from '../../modules/cities';
import { select } from 'redux-crud-store';

class Landingpage extends Component {
  componentWillMount() {
    const { cities, dispatch } = this.props;
    if (cities.needsFetch) {
      dispatch(cities.fetch);
    }
  }

  componentWillReceiveProps(nextProps) {
    const { cities } = nextProps;
    const { dispatch } = this.props;
    if (cities.needsFetch) {
      dispatch(cities.fetch);
    }
  }

  render() {
    const { cities } = this.props;
    return (
      <div>
        <section
          className={
              `hero hero-position-bottom hero-spacing ${styles['hero-landingpage']}`
          }
        >
          <div className="container">
            <h1 className="h-font text-primary">XXX</h1>
            <h2 className="h-font">XXX</h2>

            <div className={styles['landingpage-form']}>
              <div className={styles['landingpage-form-city']}>
                <select className="c-select">
                  <option value="">Choose your city</option>
                  {cities && cities.map((city) => (
                    <option key={city.slug} value={city.slug}>{city.name}</option>
                  ))}
                </select>
              </div>
              <div className={styles['landingpage-form-action']}>
                <button type="submit" className="btn btn-primary btn-lg">
                  Discover
                </button>
              </div>
              <div className={styles['landingpage-form-action']}>
                <a href="#" className="btn btn-primary-outline btn-lg">
                  Free Sign Up
                </a>
              </div>
            </div>
          </div>
        </section>
      </div>
    );
  }
}

Landingpage.propTypes = {
  cities: PropTypes.object,
  dispatch: PropTypes.func
};

function mapStateToProps(state) {
  return { cities: select(fetchCities(), state.models) };
}

export default connect(mapStateToProps)(Landingpage);

modules/cities.js

import { fetchCollection } from 'redux-crud-store';

const MODEL = 'cities';
const PATH = '/cities';

export function fetchCities(params = {}) {
  return fetchCollection(MODEL, PATH, params);
}
@mxmtsk
Copy link
Author

mxmtsk commented Jul 19, 2016

Nevermind, i got this to work (i missed "model: " in combineReducer but it throws this now:

[1]   type: 'redux-crud-store/crud/FETCH_ERROR',
[1]   payload: 
[1]    ReferenceError: URLSearchParams is not defined
[1]        at ApiClient.queryString (/Users/maxmitschke/work/wtc-web/node_modules/redux-crud-store/lib/ApiClient.js:100:19)
[1]        at _this.(anonymous function) (/Users/maxmitschke/work/wtc-web/node_modules/redux-crud-store/lib/ApiClient.js:83:51)
[1]        at runCallEffect (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:446:19)
[1]        at runEffect (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:375:399)
[1]        at next (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:259:9)
[1]        at proc (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:214:3)
[1]        at runForkEffect (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:523:16)
[1]        at runEffect (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:375:577)
[1]        at next (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:259:9)
[1]        at currCb (/Users/maxmitschke/work/wtc-web/node_modules/redux-saga/lib/internal/proc.js:329:7),
[1]   error: true }

Edit: Actually it didn't resolve the first errors. The URLSearchParameters error happens in my console, the other in the browser.

I tried to rewrite the purpose of the function in ApiClient.js to get rid of it with this:

  _createClass(ApiClient, [{
    key: 'queryString',
    value: function queryString(params) {
      var str = [];
      var s = '';
      for(var p in params) {
        if (params.hasOwnProperty(p)) {
          str.push(encodeURIComponent(p) + "=" + encodeURIComponent(params[p]));
        }
      }

      var s = str.join("&");

      return s ? '?' + s : '';
    }
  }]);

This resolved the error but now this happens:

[1] { otherInfo: {},
[1]   data: [],
[1]   isLoading: true,
[1]   needsFetch: true,
[1]   fetch: 
[1]    { type: 'redux-crud-store/crud/FETCH',
[1]      meta: 
[1]       { success: 'redux-crud-store/crud/FETCH_SUCCESS',
[1]         failure: 'redux-crud-store/crud/FETCH_ERROR',
[1]         params: {},
[1]         model: 'cities' },
[1]      payload: { method: 'get', path: '/cities', params: {} } } }
[1] action @ 01:21:29.878 redux-crud-store/crud/FETCH_SUCCESS 
[1] %c prev state color: #9E9E9E; font-weight: bold { models: Map { "cities": Map { "collections": List [ Map { "params": Map {}, "otherInfo": Map {}, "ids": List [], "fetchTime": 0, "error": null } ], "byId": Map {} } },
[1]   entities: { users: {}, repos: {} },
[1]   pagination: { starredByUser: {}, stargazersByRepo: {} },
[1]   errorMessage: null,
[1]   router: { pathname: '/', params: {} } }
[1] %c action color: #03A9F4; font-weight: bold { meta: 
[1]    { success: 'redux-crud-store/crud/FETCH_SUCCESS',
[1]      failure: 'redux-crud-store/crud/FETCH_ERROR',
[1]      params: {},
[1]      model: 'cities',
[1]      fetchTime: 1468970489622 },
[1]   type: 'redux-crud-store/crud/FETCH_SUCCESS',
[1]   payload: 
[1]    [ { id: 1,
[1]        name: 'Berlin',
[1]        country: 'Germany',
[1]        default: 1,
[1]        slug: 'berlin' },
[1]      { id: 2,
[1]        name: 'Hamburg',
[1]        country: 'Germany',
[1]        default: 0,
[1]        slug: 'hamburg' } ] }
[1] %c error color: #F20404; font-weight: bold TypeError: Cannot read property 'map' of undefined
[1]     at collectionReducer (/Users/maxmitschke/work/wtc-web/node_modules/redux-crud-store/lib/reducers.js:117:36)
[1]     at /Users/maxmitschke/work/wtc-web/node_modules/redux-crud-store/lib/reducers.js:154:16
[1]     at updateInDeepMap (/Users/maxmitschke/work/wtc-web/node_modules/immutable/dist/immutable.js:1973:22)
[1]     at updateInDeepMap (/Users/maxmitschke/work/wtc-web/node_modules/immutable/dist/immutable.js:1982:23)
[1]     at List.Map.updateIn (/Users/maxmitschke/work/wtc-web/node_modules/immutable/dist/immutable.js:1280:26)
[1]     at List.Map.update (/Users/maxmitschke/work/wtc-web/node_modules/immutable/dist/immutable.js:1272:14)
[1]     at collectionsReducer (/Users/maxmitschke/work/wtc-web/node_modules/redux-crud-store/lib/reducers.js:153:20)
[1]     at /Users/maxmitschke/work/wtc-web/node_modules/redux-crud-store/lib/reducers.js:246:16
[1]     at updateInDeepMap (/Users/maxmitschke/work/wtc-web/node_modules/immutable/dist/immutable.js:1973:22)
[1]     at updateInDeepMap (/Users/maxmitschke/work/wtc-web/node_modules/immutable/dist/immutable.js:1982:23)
[1] %c next state color: #4CAF50; font-weight: bold { models: Map { "cities": Map { "collections": List [ Map { "params": Map {}, "otherInfo": Map {}, "ids": List [], "fetchTime": 0, "error": null } ], "byId": Map {} } },
[1]   entities: { users: {}, repos: {} },
[1]   pagination: { starredByUser: {}, stargazersByRepo: {} },
[1]   errorMessage: null,
[1]   router: { pathname: '/', params: {} } }

I guess I'm doing something very very very wrong but I absolutely cannot figure it out.

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

Hi! thanks for reporting this issue!

I did test the default ApiClient, but only minimally. So chances are this is a stupid issue that can be resolved easily.

For now, could you try adding the sample ApiClient from https://github.com/uniqueway/redux-crud-store/blob/feature/api-client/docs/Sample-Api-Client-with-Superagent.md to your codebase, and then running npm install --save superagent? I'm hoping that should work for you.

I'll try to set up a better testing environment for the latest master release and make sure it all works.

As for the React performance measurement code, is this a feature of a more recent version of react? Or do you think this is coming from a library you've included? Send me any clues that will help me track this down

@devvmh devvmh added the bug label Jul 20, 2016
@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

@mxmtsk could you take a look at facebook/react#6949 and see if it's related to your problem at all? I haven't used react perf at all, but it looks related. It might help me better understand where the issue comes from if you look into this for me. thanks!!

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

Ah ha. looks like I messed up by including URLSearchParams in this library because of browser support: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams. IE and Safari don't have it. I've got a commit (c6804d8) fixing that ready for testing alongside the react-perf stuff

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

@mxmtsk could you send me the rough outline of the format of the data that comes from the server?

Currently a big limitation of this library is that it expects your response to have a "data envelope" like this:

{ data: { ... actual response ... } }

for every response. I'm aware that's awful, and I want to set up normalizr integration (see #19) so that this is customizable.

If you are able to implement your own ApiClient, you should be able to customize what it returns to provide something in that format. I could also consider a temporary solution of checking the response for a data key and if it doesn't include one, wrapping it in a data envelope.

Does that make sense to you? Are you able to test if that solution works? If it does, I could add this to the library as a workaround until #19 is implemented.

@mxmtsk
Copy link
Author

mxmtsk commented Jul 20, 2016

Hey,
Your fix does work (Although the /lib/ folder was not updated and I had to build myself)

Indeed, I do not have a data wrapper around my response, I added it for testing purposes and it will process data, but the initial error will not go away. As i understood the discussion correctly in the react repo it seems that the performance error get's thrown by the following error (getIn...). This error changed from Uncaught TypeError: Cannot read property 'getIn' of undefined to Uncaught TypeError: crud.getIn is not a function (@selectors.js:71).

I think this is an immutable problem. When I added crud = _immutable.Map(crud); right before the line in selectors.js:70 it will move on and throw the same error in line 71 with model.get.

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

so in mapStateToProps you're doing something like this, right?

const cities = selectCollection('cities', state.models)

What does console.log(state.models) return? Is it an Immutable map? I can't think of a reason why it wouldn't be an immutable map, unless you're doing something I haven't seen before.

Could you post your package.json? (or at least the dependencies part)

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

Ah, I reviewed your code more closely now.

I haven't used the select function extensively, it's an addition by @hallettj . Maybe he has some insight. In the meantime, what about changing your mapStateToProps code to this to see what happens:

function mapStateToProps(state) {
  return { cities: selectCollection('cities', state.models) };
}

UPDATE: I've reviewed select a bit more thoroughly, and it does work like I expected, so probably this won't help. Ah ha, you've posted below - it didn't help.

@mxmtsk
Copy link
Author

mxmtsk commented Jul 20, 2016

It returns Object {cities: Object}

I am using xkawi/react-universal-saga as a boilerplate. My deps don't differ right now from the boilerplate as I'm just playing around.

Changing it to selectCollection does not change anything, same errors

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

I'll clone that repo and see if I can duplicate the error

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

OK I've duplicated your boilerplate and gotten the error.

I'm still not sure why it happens, but a workaround is to import fromJS from immutable and change mapStateToProps:

import { fromJS } from 'immutable'
...
function mapStateToProps(state) {
  return { cities: select(fetchCities(), fromJS(state.models)) };
}

You'll also need to change the line that says cities && cities.map... to read cities.data.map...

I'm going to keep digging to figure out why it isn't being stored as immutable in the first place. My one hunch right now is the server side rendering

UPDATE: currently my issue is that the FETCH is dispatched by the server, and then no FETCH_SUCCESS or FETCH_ERROR comes back. It must be a saga issue. Let me know if you need my code to get this far

@mxmtsk
Copy link
Author

mxmtsk commented Jul 20, 2016

I managed to get to the point that I don't get an error back but cities.data is empty when I console.log it in my component render. is that what you experience too?

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

Yes.

I think the problem is with the server side rendering. I don't know how you set up your sagas, but because this boilerplate has its own sagas, you need to fork the two sagas. My current code does the fork on the client side, but that means it's important to NOT dispatch fetch on the server side. It's probably important that async actions should only be dispatched on the client side.

I don't have any experience with server side rendering, but hopefully there's a way to trigger the action on the client side render. I wonder how that's accomplished normally...

@mxmtsk
Copy link
Author

mxmtsk commented Jul 20, 2016

Server-Side-Rendering is what get's me frustrated all the time too... at least we're sitting in the same boat :D

In the boilerplate on this line there is an example with preload. I think this is for SSR. It get's called in the server.js. I tried to apply the crudSaga to preload but that didn't work...

@devvmh
Copy link
Owner

devvmh commented Jul 20, 2016

OK I can't spend more time on this today. I think I've almost got it, but I have an issue with babel-polyfill being missing that throws Uncaught ReferenceError: regeneratorRuntime is not defined. I've opened a new PR on this repo removing babel-polyfill from this repo that I can merge if it helps this sort of issue. You can see what I did to replicate the issue on my fork's master branch: xkawi/react-universal-saga@master...devvmh:master

If you are able to find the solution and propose a fix here that would help smooth the process for users in the future, I will be eternally grateful

@devvmh devvmh changed the title React Performance Measurement Error server-side rendering and sagas issue (originally "React Performance Measurement Error") Jul 25, 2016
@devvmh
Copy link
Owner

devvmh commented Aug 4, 2016

I think my conclusion is that the issue is that you can't do AJAX in the server code. I think it's possible for this library to handle this + SSR, but for now, it's out of scope, so I'm going to close this issue.

I think https://www.npmjs.com/package/redux-async-connect will be able to help you if you want to solve this problem.

I'd love to hear back from you if a) you think there is a different reason for this that I can solve in this library or b) you figure out how to solve it and want to share the solution.

Thanks for putting the code through the ropes! It's much appreciated!

@devvmh devvmh closed this as completed Aug 4, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants