catamphetamine / react-pages

A complete solution for building a React/Redux application: routing, page preloading, (optional) server-side rendering, asynchronous HTTP requests, document metadata, etc.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Moving from isomorphic to website causes render service error

russellbits opened this issue · comments

I am upgrading a site currently running the react-isomorphic-render package to react-website. After changing all the reducers to the new react-website style (among other adjustments) I am now getting this not so helpful error in the console:

Render service is listening at port 3002
(node:25020) Warning: a promise was rejected with a non-error: [object Object]
(node:25020) Warning: a promise was rejected with a non-error: [object Object]
(node:25020) Warning: a promise was rejected with a non-error: [object Object]
(node:25020) Warning: a promise was rejected with a non-error: [object Object]
(node:25020) Warning: a promise was rejected with a non-error: [object Object]
[react-website] Rendering service error

Even more difficult is that web site itself simply appears as the word Error with nothing in the console.

One possible problem I see is that preload was a reducer function with the old package. Should that reducer just be eliminated now?

Alternately, since this appears to be a promise problem, could this have anything to do with the site using bluebird for promises?

You could start with commenting out all @preload()s for a single route and then see if the error disappears.
If it disappears then uncomment those @preload()s from top to bottom and see which one errors.
Then comment it out and then uncomment it line-by-line to find the erroring piece of code.

Thanks for the suggestion. I tried this—reducing the routes down to just the IndexRoute and commenting out any @preload function there but I still get the above errors. Incidentally the a promise was rejected with a non-error: [object Object] type errors do not show up until the page is reloaded in the browser. I tried shutting off javascript in the browser to get the server-side render to act alone. The whole HTML page just says "Error" with nothing in the console.

@russellbits So it's not coming from a @preload() promise then...
And it happens in the server-side part of the code.

If you look at the source code in the library it's this:

./node_modules/react-pages/commonjs/server/renderError.js

import { html } from 'print-error'

// Outputs an error page
export default function renderError(error, options)
{
	// If the error is caught here it means that `catch`
	// (error handler parameter) didn't resolve it
	// (or threw it)

	// Show error stack trace in development mode for easier debugging
	if (process.env.NODE_ENV !== 'production') {
		try {
			const { status = 500, content } = renderStackTrace(error, options)

			if (content) {
				return {
					status,
					content,
					contentType: 'text/html'
				}
			}
		} catch (error) {
			console.log('[react-pages] Couldn\'t render error stack trace.')
			console.log(error.stack || error)
		}
	}

	// Log the error
	console.log('[react-pages] Rendering service error')
	// console.error(error)

	return {
		status: typeof error.status === 'number' ? error.status : 500,
		content: error.message || 'Error',
		contentType: 'text/plain'
	}
}

function renderStackTrace(error, options)
{
	// Supports custom `html` for an `Error`
	if (error.html) {
		return {
			status  : error.status,
			content : error.html
		}
	}

	// Handle `superagent` errors: if an error response was an html, then just render it
	// https://github.com/visionmedia/superagent/blob/29ca1fc938b974c6623d9040a044e39dfb272fed/lib/node/response.js#L106
	if (typeof error.status === 'number') {
		// If the `superagent` http request returned an html response
		// (possibly an error stack trace),
		// then just output that stack trace
		if (error.response
			&& error.response.headers['content-type']
			&& error.response.headers['content-type'].split(';')[0].trim() === 'text/html') {
			return {
				status  : error.status,
				content : error.message
			}
		}
	}

	// If this error has a stack trace then it can be shown
	const stack = getStackTrace(error)

	// If this error doesn't have a stack trace - do nothing
	if (!stack) {
		return {}
	}

	try {
		return {
			content : html({ stack }, options)
		}
	} catch (error) {
		console.error(error)
		return {
			content : error.stack
		}
	}
}

// Extracts stack trace from `Error`
function getStackTrace(error)
{
	// Standard javascript `Error` stack trace
	if (error.stack) {
		return error.stack
	}

	// `superagent` errors have the `original` property
	// for storing the initial error
	if (error.original && error.original.stack) {
		return error.original.stack
	}
}

You see there's a console.error() commented out there.
You can uncomment it and see what it outputs.

Thank you. I could not find that error line before. Now I get
HttpError { status: 404, data: undefined } GET / 404 197.134 ms - 5

That's a bit more helpful.

Does the @preload decorator have to use async/await? If it's not, could that be causing the Warning: a promise was rejected with a non-error: [object Object]

Does the @preload decorator have to use async/await?

It's not clear what you mean.

In your docs you mention "@preload() decorator takes an async/await function which takes an object of arguments," say, {preloadArugments}. I'm wondering if that always has to be the case.

Yes. It doesn't say can take, it says takes.

Do you recall if that was the case with the older package (react-isomorphic-render)?

I guess it wasn't but no guarantees. There has been a lot of previous versions.

Your server-side-render example does not use the same asynSettings that my app does.

import {underscoredToCamelCase} from 'react-website';

export default {
    // When supplying `event` instead of `events`
    // as part of an asynchronous Redux action
    // this will generate `events` from `event`
    // using this function.
    asynchronousActionEventNaming: (evt) => ([
        `${evt}_PENDING`,
        `${evt}_SUCCESS`,
        `${evt}_ERROR`
    ]),

    // When using `asynchronousActionHandler`
    // this function will generate a Redux state property name from an event name.
    // E.g. event `GET_USERS_ERROR` => state.`getUsersError`.
    asynchronousActionHandlerStatePropertyNaming: underscoredToCamelCase
};

This gets imported into my more typical react-website.js settings along with reducers. I'm not sure if this could be interfering with react-website's built-in events for preload...

asyncSettings are no longer required.
I'd suggest removing them from code.