Closed donut closed 7 years ago
Use createInitialFarceRouter
per https://github.com/4Catalyzer/found-relay/blob/master/examples/todomvc-modern-universal/src/client.js#L21.
createInitialBrowserRouter
ignores custom resolvers.
That worked! I looked over the example so many times and just kept missing that difference. Thank you!
Adding it to a list of things to document in https://github.com/4Catalyzer/found-relay/issues/56
Im migrating from react-router to found-relay (ssr with codesplitting by route) and relay modern. I was unable to incrementally migrate to modern (lot of fragment and environment issues) so i decided to make the app as we have it now, work in the stack that can support modern, that way we can take our time with the migration.
However, im running into issues with context. The app seemingly renders correctly but passes the relay modern environment instead of the classic environment as the context in Component.context.relay.. any clue where I may have went wrong?
I've been looking everywhere trying to identify the problem but have come up empty handed. I can create a new issue if necessary, I just thought i'd try here to see if it was related in any way before polluting your issues.
the component requires the relay context in this way:
CheckingComponent.contextTypes = {
router: PropTypes.object,
relay: Relay.PropTypes.Environment,
}
console error:
Warning: Failed context type: Invalid prop/context `relay` supplied to `CheckingComponent`, expected `[object Object]` to be an object conforming to the `RelayEnvironment` interface.
in CheckingComponent (created by Relay(CheckingComponent))
in Relay(CheckingComponent) (created by RelayReadyStateRenderer)
in StaticContainer (created by RelayReadyStateRenderer)
in RelayReadyStateRenderer (created by RelayRouteRenderer)
in RelayRouteRenderer
in ElementsRenderer (created by BaseRouter)
in StaticContainer (created by BaseRouter)
in StaticContainer (created by BaseRouter)
in BaseRouter (created by Connect(BaseRouter))
in Connect(BaseRouter) (created by FarceRouter)
in Provider (created by FarceRouter)
in FarceRouter
router.jsx
import React from 'react'
//classic
import Relay from 'react-relay/classic'
import {
RelayNetworkLayer,
urlMiddleware,
} from 'react-relay-network-layer'
import cookie from 'react-cookie'
import {
GRAPHQL_SERVER_URL
} from '../configuration/webapp/config'
import queryMiddleware from 'farce/lib/queryMiddleware'
import makeRouteConfig from 'found/lib/makeRouteConfig'
import Route from 'found/lib/Route'
import schema from '../schema/schema.json'
// import App from './scenes'
import routesRoot from './routes'
export const historyMiddlewares = [queryMiddleware]
//classic
//reqToken contains req.token for server side rendering
export function createEnvironment(reqToken) {
const environment = new Relay.Environment()
let token = reqToken || cookie.load('token')
environment.injectNetworkLayer(
new RelayNetworkLayer([
urlMiddleware({
url: req => GRAPHQL_SERVER_URL,
}),
next => req => {
req.headers = {
token,
credentials: 'same-origin',
}
if (reqToken) {
req.headers['Content-type'] = 'application/json'
}
return next(req)
},
]),
)
return environment
}
export const routeConfig = makeRouteConfig(routesRoot)
server.js
// @flow
// import { JssProvider, SheetsRegistry } from 'react-jss'
import path from 'path'
import createRender from 'found/lib/createRender'
import { getFarceResult } from 'found/lib/server'
import express from 'express'
import Helmet from 'react-helmet'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import serialize from 'serialize-javascript'
import { Resolver } from 'found-relay/lib/classic'
import {
GRAPHQL_SERVER_URL,
} from '../configuration/webapp/config'
import ErrorComponent from './ErrorComponent'
import { createEnvironment, historyMiddlewares, routeConfig } from './router'
// Read environment
require('dotenv').load()
const envPortWebpack = process.env.PORT_WEBPACK
const server = express()
async function gatherLocationAndSiteInformation(req: Object, res: Object) {
let assetsPath
if (process.env.NODE_ENV === 'production') {
assetsPath = `/`
} else {
assetsPath = `http://localhost:${envPortWebpack}/dev`
}
return { assetsPath }
}
const render = createRender({
renderError(obj: Object): React$Element<*> {
const { error } = obj
return <ErrorComponent httpStatus={error.status} />
},
})
server.use(async (req, res) => {
try {
const { assetsPath } = await gatherLocationAndSiteInformation(req, res)
const environment = createEnvironment(req.cookies.token)
const { redirect, status, element } = await getFarceResult({
url: req.url,
historyMiddlewares,
routeConfig,
resolver: new Resolver(environment),
render,
})
if (redirect) {
res.redirect(302, redirect.url)
return
}
const helmet = Helmet.rewind()
const rootHTML = ReactDOMServer.renderToString(element)
res.render(
path.resolve(
__dirname,
process.env.NODE_ENV === 'production'
? 'renderOnServer.ejs'
: 'renderOnServer-template.ejs',
),
{
assets_path: assetsPath,
root_html: rootHTML,
helmet,
relay_payload: serialize(environment, { isJSON: true })
},
)
} catch (err) {
log.log('error', 'Error: Render on server request', err)
res.status(500).send(ReactDOMServer.renderToString(<ErrorComponent httpStatus={500} />))
}
})
export default server
client.js
// @flow
// In order to use ES7 async/await
import 'babel-polyfill'
import BrowserProtocol from 'farce/lib/BrowserProtocol'
import createInitialFarceRouter from 'found/lib/createInitialFarceRouter'
import createRender from 'found/lib/createRender'
import React from 'react'
import Relay from 'react-relay/classic'
import ReactDOM from 'react-dom'
import injectTapEventPlugin from 'react-tap-event-plugin'
import { GRAPHQL_SERVER_URL } from '../configuration/webapp/config'
import { createEnvironment, historyMiddlewares, routeConfig } from './router'
import { Resolver } from 'found-relay/lib/classic'
const render = createRender({})
;(async () => {
try {
injectTapEventPlugin()
} catch (e) {
console.log('already injected tap plugin', e)
}
const resolver = new Resolver(createEnvironment())
const Router = await createInitialFarceRouter({
historyProtocol: new BrowserProtocol(),
historyMiddlewares,
routeConfig,
resolver,
render,
})
ReactDOM.render(<Router />, document.getElementById('root'))
})()
==== more supporting files ====
before found:
after found:
@taion I would deeply appreciate any insight you may have on this issue
v1 changes the shape of context.relay
, even when using classic mode. See e.g. the changes I made for https://github.com/edvinerikson/relay-subscriptions/pull/35/files#diff-ca27f0129c75f4c86b6b281186ae94c3.
That's a change in Relay proper, BTW – nothing to do directly with this library.
Oh wow! Thank you very much, adapting to Relay.PropTypes.ClassicRelay
solves my issue.
I'm writing an isomorphic app and the server is rendering everything fine, but in the browser (Chrome) I get these errors:
I'm trying to follow the todo app example, which is running fine for me, but I can't figure out what I'm doing differently. It just seems that the
relay
context isn't getting passed to the components for some reason.I'm using found-relay@0.3.0-alpha.4
Browser entry point (browser.tsx):
Router/App File:
VideoList
component: