relay: [Modern] connection is undefined when using multiple QueryRenderers
I’m using React Router v2.8.1 and have the following simple setup for my app:
ReactDOM.render(
<Router history={browserHistory}>
<Route path='/' component={App} />
<Route path='/create' component={CreatePage} />
</Router>
, document.getElementById('root')
)
Both App
and CreatePage
have as a root a QueryRenderer
.
App
const AppAllPostQuery = graphql`
query AppAllPostQuery {
viewer {
...ListPage_viewer
}
}
`
class App extends Component {
render() {
return (
<QueryRenderer
environment={environment}
query={AppAllPostQuery}
render={({error, props}) => {
if (error) {
return <div>{error.message}</div>
} else if (props) {
return <ListPage viewer={props.viewer} />
}
return <div>Loading</div>
}}
/>
)
}
}
ListPage
is a FragmentContainer
that renders a list of Post
s. Here’s how the fragment containers for ListPage
and Post
are defined with their fragments:
ListPage
export default createFragmentContainer(ListPage, graphql`
fragment ListPage_viewer on Viewer {
...Post_viewer
allPosts(last: 100, orderBy: createdAt_DESC) @connection(key: "ListPage_allPosts", filters: []) {
edges {
node {
id
description
imageUrl
...Post_post
}
}
}
}
`)
Post
export default createFragmentContainer(Post, graphql`
fragment Post_viewer on Viewer {
id
}
fragment Post_post on Post {
id
description
imageUrl
}
`)
CreatePage
const CreatePageViewerQuery = graphql`
query CreatePageViewerQuery {
viewer {
id
}
}
`
class CreatePage extends React.Component {
state = {
description: '',
imageUrl: '',
}
render () {
return (
<QueryRenderer
environment={environment}
query={CreatePageViewerQuery}
render={({error, props}) => {
if (error) {
return <div>{error.message}</div>
} else if (props) {
return (
<div className='w-100 pa4 flex justify-center'>
// ...
</div>
)
}
return <div>Loading</div>
}}
/>
)
}
_handlePost = (viewerId) => {
const {description, imageUrl} = this.state
console.log(`New Post: `, viewerId)
CreatePostMutation(description, imageUrl, viewerId, () => this.props.router.replace('/'))
}
}
Now, when I’m deleting a post and use the updater
, everything works as expected and the deleted post gets removed from the store:
updater: (proxyStore) => {
const deletePostField = proxyStore.getRootField('deletePost')
const deletedId = deletePostField.getValue('deletedId')
const viewerProxy = proxyStore.get(viewerId)
const connection = ConnectionHandler.getConnection(viewerProxy, 'ListPage_allPosts')
ConnectionHandler.deleteNode(connection, deletedId)
}
However, when adding a new post, the ConnectionHandler
can’t seem to find the connection with key ListPage_allPosts
:
updater: (proxyStore) => {
const createPostField = proxyStore.getRootField('createPost')
const newPost = createPostField.getLinkedRecord('post')
const viewerProxy = proxyStore.get(viewerId)
const connection = ConnectionHandler.getConnection(viewerProxy, 'ListPage_allPosts')
// `connection` is undefined
ConnectionHandler.insertEdgeAfter(connection, newPost)
}
Since connection
is undefined
here, I’m getting the following error message in the next line:
RelayConnectionHandler.js:225 Uncaught (in promise) TypeError: Cannot read property 'getLinkedRecords' of undefined
My assumption is that connection
can’t be found here because I have two different QueryRenderers
- and the one that I’m using in this case (unlike the one for the delete-mutation) doesn’t have the ListPage_allPosts
in its tree. Is that correct?
What would be a good way to solve this issue? Can connections be shared across QueryRenderer
s? Is there a way how this can be solved in combination with React Router?
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 6
- Comments: 22 (14 by maintainers)
What’s happening here is that, out-of-the-box, Relay Modern doesn’t cache anything that isn’t being rendered.
When you go to
<CreatePage>
, you unmount<App>
, and the GC will expunge the connection from the store, sogetConnection
will correctly returnundefined
.Since in this case, the connection isn’t in the local store any more, you don’t need to update it. The correct way to handle this is to just do:
In other words, only update the store if there’s anything to update.
I’m have a similar issue with React Router v4
RootRouter.js
ListPage.js use follow query
The scenario is as follows.
/list
and click/create
link/create
location callCreateItemMutation
mutationCreateItemMutation
mutationupdater
can’t updateListPage_list
becauseListPage
unmounthistory.push("/list")
inonCompleted
and return to the/list
page, showing only old dataHow can i solve this issue? Should i set
cacheConfig
to{ force: true }
or useSubscriptions
?That makes sense.
@sibelius However, I’m still wondering, is there a way for a parent QueryRender to not be affected by the inner QueryRender? I’m assuming what’s happening here that when the inner QueryRenderer’s mounts and fetches data that blows away the parent’s fetched data, and the parent renderer gets passed the inner renderer’s data.
I recommend avoiding QueryRenderer inside QueryRenderer, the top one will rerender the below one in the tree causing another fetch