facebook / facebook/relay

CreatMutation Error for non-page Refreshing on Relay/GraphQL

Open
#2,163 3 comments 0 reactions 0 assignees View on GitHub
wontfix
Dominant language
Rust
Stars
19k
Forks
1.9k
PR merge metrics
No merged PRs in 30d

Description

# CreatMutation Error for non-page Refreshing on Relay/GraphQL
I create React-Relay APP with Graph QL.
Refer website `https://blog.graph.cool/getting-started-with-relay-modern-46f8de6bd6ec`

Instead of Post, I create Project/Hour.
Also it has one-to-many relationship.

## This is Schema of Graph QL
```sh

type Project @model {
id: ID! @isUnique
createdAt: DateTime!
updatedAt: DateTime!
name: String!
description: String!
hours: [Hour!]! @relation(name: "HoursOnProject")
}

type Hour @model {
id: ID! @isUnique
createdAt: DateTime!
updatedAt: DateTime!
amount: Int!
description: String!
project: Project @relation(name: "HoursOnProject")
}
```

I create new Data `Hour` for each `Project`.
When I try DeleteMutation, it is removing items without refreshing page.
Also In order to create `New Hour Item`, It is using React-Router.
It works well.
### Screenshot(`https://prnt.sc/h0m1lw`)
![Screenshot URL](https://image.prntscr.com/image/AShLkzykTx_cchxVLYCP8Q.png)

I am going to make CreateMutation like DeleteMutation.. Totally
```sh
1) Add Form
2) Create Item when Click button( without refreshing).. Don't use react-router
3) Show total item lists
```
### Target Screenshot URL (`https://prnt.sc/h0ly9v`)
![Target](https://image.prntscr.com/image/s2NB7sASSmmKth0G21SWNw.png)

When I try to click `Add` button, I got error like this:
```sh
HourList.js:35 Uncaught TypeError: Cannot read property '__id' of undefined
at HourList.js:35
at Array.map ()
at HourList.render (HourList.js:35)
at finishClassComponent (react-dom.development.js:10249)
at updateClassComponent (react-dom.development.js:10226)
at beginWork (react-dom.development.js:10605)
at performUnitOfWork (react-dom.development.js:12573)
at workLoop (react-dom.development.js:12682)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at performWork (react-dom.development.js:12800)
at batchedUpdates (react-dom.development.js:13244)
at performFiberBatchedUpdates (react-dom.development.js:1646)
at stackBatchedUpdates (react-dom.development.js:1637)
at batchedUpdates (react-dom.development.js:1651)
at Object.batchedUpdatesWithControlledComponents [as batchedUpdates] (react-

proxyConsole.js:54 The above error occurred in the component:
in HourList (created by Relay(HourList))
in Relay(HourList) (at HourListPage.js:48)
in ReactRelayQueryRenderer (at HourListPage.js:39)
in HourListPage (created by Route)
in Route (created by withRouter(HourListPage))
in withRouter(HourListPage) (created by Route)
in Route (at App.js:17)
in Switch (at App.js:15)
in div (at App.js:14)
in div (at App.js:12)
in App (at index.js:11)
in Router (created by BrowserRouter)
in BrowserRouter (at index.js:10)

HourList.js:35 Uncaught TypeError: Cannot read property '__id' of undefined
at HourList.js:35
at Array.map ()
at HourList.render (HourList.js:35)
at finishClassComponent (react-dom.development.js:10249)
at updateClassComponent (react-dom.development.js:10226)
at beginWork (react-dom.development.js:10605)
at performUnitOfWork (react-dom.development.js:12573)
at workLoop (react-dom.development.js:12682)
at HTMLUnknownElement.callCallback (react-dom.development.js:1299)
at Object.invokeGuardedCallbackDev (react-dom.development.js:1338)
at invokeGuardedCallback (react-dom.development.js:1195)
at performWork (react-dom.development.js:12800)
at batchedUpdates (react-dom.development.js:13244)
at performFiberBatchedUpdates (react-dom.development.js:1646)
at stackBatchedUpdates (react-dom.development.js:1637)
at batchedUpdates (react-dom.development.js:1651)
at Object.batchedUpdatesWithControlledComponents [as batchedUpdates] (react-dom.development.js:1664)

proxyConsole.js:54 Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.
Please check the code for the CreateHour component.
__stack_frame_overlay_proxy_console__ @ proxyConsole.js:54
printWarning @ warning.js:33
warning @ warning.js:57

RelayConnectionHandler.js:191 Uncaught TypeError: Cannot read property 'getLinkedRecord' of undefined
at Object.getConnection (RelayConnectionHandler.js:191)
at updater (CreateHourMutation.js:64)
at RelayPublishQueue._getSourceFromPayload (RelayPublishQueue.js:171)
at RelayPublishQueue.js:185
at Set.forEach ()

```

## Error Screenshot URL:
### Screenshot Error Page 1(`https://prnt.sc/h0m5b9`)
![Error1](https://image.prntscr.com/image/_xktJk1fQuKWqr6Porbw4Q.png)
### Screenshot Error Page 2(`https://prnt.sc/h0m5hq`
![Erro2](https://image.prntscr.com/image/KQh3uTq7Q5KI8PpgtFXa3w.png)

## Project Source
### In the `HourList.js` there are `` Component. When I add this, it is generating error.. Actually I want to make CreateMutation like DeleteMutation without Page Refreshing.

### `App.js`

```sh
class App extends Component {
render() {
return (












)
}
}

```

### `HourListPage.js`

```sh
import React, { Component } from 'react'
import { withRouter } from 'react-router'
import {
QueryRenderer,
graphql
} from 'react-relay'
import environment from '../Environment'
import HourList from "./HourList"

const HourListPageQuery = graphql`
query HourListPageQuery (
$filter: HourFilter,
$id:ID!
)
{
viewer {
...HourList_viewer
}
}
`

class HourListPage extends Component{
variables = {}
proj_id = ""
componentWillMount(){
this.proj_id = this.props.match.params.proj_id;
this.variables = {
filter: {
project:{
id : this.proj_id
}
},
id : this.proj_id
}
}
render() {
return(
{
// console.log(props);
if (error) {
return

{error.message}

} else if (props) {
return
}
return
Loading

}}
/>
)
}
}

export default withRouter(HourListPage)
```

### `HourList.js`

```sh
import React from 'react'
import Hour from './Hour'
import {
createFragmentContainer,
graphql
} from 'react-relay'
import {Link} from 'react-router-dom'
import CreateHour from "./CreateHour"
class ProjectDesc extends React.Component{

render () {
return (


{this.props.project.name}


{this.props.project.description}


Create Hour


)
}
}
class HourList extends React.Component {
render () {
if(this.props.viewer.allHours.edges.length === 0){
return (


No Result


)
}
return (


{this.props.viewer.allHours.edges.map(({node}, index) =>

)}

)
}
}

export default createFragmentContainer(HourList, graphql`
fragment HourList_viewer on Viewer {
...Hour_viewer
Project(id:$id){
id
name
description
}
allHours(filter:$filter,last: 100, orderBy: createdAt_DESC) @connection(key: "HourList_allHours", filters: []) {
edges {
node {
...Hour_hour
}
}
}
}
`)
```

### `Hour.js`

```sh
import React from 'react'
import {
createFragmentContainer,
graphql
} from 'react-relay'

import DeleteHourMutation from '../mutations/DeleteHourMutation'

class Hour extends React.Component {
// componentWillMount(){
// console.log(this.props);
// }
render () {
return (



{this.props.index + 1}.
{this.props.hour.description} 
{this.props.hour.amount}  Hours
Delete


)
}

_handleDelete = () => {
DeleteHourMutation(this.props.hour.id, this.props.viewer.id)
}
}
export default createFragmentContainer(Hour, graphql`
fragment Hour_viewer on Viewer {
id
}
fragment Hour_hour on Hour {
id
description
amount
}
`)
```

### `CreateHourMutation.js`

```sh
// 1
import {
commitMutation,
graphql,
} from 'react-relay'
import {ConnectionHandler} from 'relay-runtime'
import environment from '../Environment'
// 2
const mutation = graphql`
mutation CreateHourMutation($input: CreateHourInput!) {
createHour(input: $input) {
hour {
id
createdAt
amount
description
}
}
}
`
let tempHourID = 0
// 3
export default (description, amount, projectId, viewerId, callback) => {
// 4
const variables = {
input: {
description,
amount,
projectId,
clientMutationId: ""
},
}
// 5
commitMutation(
environment,
{
mutation,
variables,
// 6
optimisticUpdater: (proxyStore) => {
// 1 - create the `newHour` as a mock that can be added to the store
const id = 'client:newHour:' + tempHourID++
const newHour = proxyStore.create(id, 'Hour')
newHour.setValue(id, 'id')
newHour.setValue(description, 'description')
newHour.setValue(parseInt(amount), 'amount')
newHour.setValue(projectId, 'projectId')
// 2 - add `newHour` to the store
const viewerProxy = proxyStore.get(viewerId)

const connection = ConnectionHandler.getConnection(viewerProxy, 'HourList_allHours', [])

if (connection) {
ConnectionHandler.insertEdgeAfter(connection, newHour)
}
},
updater: (proxyStore) => {
//1 - retrieve the `newHour` from the server response
const createHourField = proxyStore.getRootField('createHour')
// console.log(createHourField);
const newHour = createHourField.getLinkedRecord('hour')
// 2 - add `newHour` to the store
const viewerProxy = proxyStore.get(viewerId)
const connection = ConnectionHandler.getConnection(viewerProxy, 'HourList_allHours', [])

if (connection) {
ConnectionHandler.insertEdgeAfter(connection, newHour)
}
},
// 7
onCompleted: () => {
callback()
},
onError: err => console.error(err),
},
)
}
```

### `CreateHour.js`

```sh
import React from 'react'
import { withRouter } from 'react-router'
import CreateHourMutation from '../mutations/CreateHourMutation'
import { QueryRenderer, graphql } from 'react-relay'
import environment from '../Environment'
import {Redirect} from 'react-router-dom'

const CreateHourViewerQuery = graphql`
query CreateHourViewerQuery {
viewer {
id
}
}
`
class CreateHour extends React.Component {
state = {
description: '',
amount: 0,
projectId: '',
submitted: false
}
componentWillMount(){
if (this.props.match.params.proj_id)
this.setState({projectId : this.props.match.params.proj_id});
}
render () {
if (this.state.submitted)
return ()

return (
{
if (error) {
return

{error.message}

} else if (props) {
return (


this.setState({description: e.target.value})}
/>
this.setState({amount: e.target.value})}
/>
this._handleHour(props.viewer.id)}>Add


)
}
return
Loading

}}
/>
)
}
_handleHour = (viewerId) => {
const {description, amount, projectId} = this.state
console.log(this.state)
CreateHourMutation(description, parseInt(amount), projectId, viewerId, () => this.setState({submitted:true}))
}
}
export default withRouter(CreateHour)
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.