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 Posts. 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 QueryRenderers? 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, sogetConnectionwill 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.
/listand click/createlink/createlocation callCreateItemMutationmutationCreateItemMutationmutationupdatercan’t updateListPage_listbecauseListPageunmounthistory.push("/list")inonCompletedand return to the/listpage, showing only old dataHow can i solve this issue? Should i set
cacheConfigto{ 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