{"action":"closed","issue":{"url":"https://api.github.com/repos/Shopify/quilt/issues/215","repository_url":"https://api.github.com/repos/Shopify/quilt","labels_url":"https://api.github.com/repos/Shopify/quilt/issues/215/labels{/name}","comments_url":"https://api.github.com/repos/Shopify/quilt/issues/215/comments","events_url":"https://api.github.com/repos/Shopify/quilt/issues/215/events","html_url":"https://github.com/Shopify/quilt/issues/215","id":346742777,"node_id":"MDU6SXNzdWUzNDY3NDI3Nzc=","number":215,"title":"[react-form-state] Unmounting FormState on submit handler throws a warning","user":{"login":"amireynoso","id":1855829,"node_id":"MDQ6VXNlcjE4NTU4Mjk=","avatar_url":"https://avatars1.githubusercontent.com/u/1855829?v=4","gravatar_id":"","url":"https://api.github.com/users/amireynoso","html_url":"https://github.com/amireynoso","followers_url":"https://api.github.com/users/amireynoso/followers","following_url":"https://api.github.com/users/amireynoso/following{/other_user}","gists_url":"https://api.github.com/users/amireynoso/gists{/gist_id}","starred_url":"https://api.github.com/users/amireynoso/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/amireynoso/subscriptions","organizations_url":"https://api.github.com/users/amireynoso/orgs","repos_url":"https://api.github.com/users/amireynoso/repos","events_url":"https://api.github.com/users/amireynoso/events{/privacy}","received_events_url":"https://api.github.com/users/amireynoso/received_events","type":"User","site_admin":false},"labels":[{"id":1038274199,"node_id":"MDU6TGFiZWwxMDM4Mjc0MTk5","url":"https://api.github.com/repos/Shopify/quilt/labels/Package:%20react-form-state","name":"Package: react-form-state","color":"202E78","default":false},{"id":881658184,"node_id":"MDU6TGFiZWw4ODE2NTgxODQ=","url":"https://api.github.com/repos/Shopify/quilt/labels/Type:%20Bug%20%F0%9F%90%9B","name":"Type: Bug 🐛","color":"FFC58B","default":false}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2018-08-01T19:48:32Z","updated_at":"2018-11-28T20:30:52Z","closed_at":"2018-11-28T20:30:52Z","author_association":"NONE","body":"### Problem\r\nTriggering an action that unmounts `<FormState>` in passed the submit handler triggers the following React warning: \r\n\r\n`Warning: Can't call setState (or forceUpdate) on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.`\r\n\r\nThis is because the [submit handler is executed](https://github.com/Shopify/quilt/blob/master/packages/react-form-state/src/FormState.ts#L169) before calling `setState` to set `submitting: false`, so if the handler unmounts `<FormState>` there's no point in continuing.\r\n\r\n### Use case\r\nIn my case, I want to redirect the user to a different page after a successful submit:\r\n\r\n```tsx\r\n@autobind\r\n  private async handleSubmit({fields}: FormData<Fields>) {\r\n    const {mutate, history} = this.props;\r\n\r\n    const {\r\n      data: {updateTeam: payload},\r\n    } = await mutate({\r\n      variables: {\r\n        id: this.props.team.id,\r\n        team: {\r\n          description: fields.description.value,\r\n          missionStatement: fields.missionStatement.value,\r\n        },\r\n      },\r\n    });\r\n\r\n    if (payload) {\r\n      if (payload.userErrors.length) {\r\n        // do something with errors\r\n        return;\r\n      }\r\n      \r\n      // successful save\r\n      if (payload.team) {\r\n        history.push('/details');\r\n      }\r\n    }\r\n  }\r\n```\r\n\r\nThis will get executed before FormState finishes the submit process.\r\n\r\nThis is also true for anything that triggers a re-render which unmounts `<FormState>`. For example, I also tried setting the state to something like `formSubmitted` and then rendering a `<Redirect>` component.\r\n\r\n> ☝️ The above would have worked (or so I thought) in conjunction with FormState's exposed `submitting` prop, but I had no luck.\r\n\r\nCodeSandbox: https://codesandbox.io/s/jlljj34ylv\r\n\r\n### Proposed solution\r\nWhat did work for me, and thought it might be a good practice, is to set a guard check in FormState's  `submit()`, after the user's submit handler gets executed, checking if it is still `mounted`. \r\n\r\nSomething like this [around here](https://github.com/Shopify/quilt/blob/master/packages/react-form-state/src/FormState.ts#L169):\r\n\r\n```tsx\r\nprivate async submit(event?: Event) {\r\n    const {onSubmit} = this.props;\r\n    const {formData, mounted} = this;\r\n\r\n    if (!mounted) {\r\n      return;\r\n    }\r\n\r\n    if (event && !event.defaultPrevented) {\r\n      event.preventDefault();\r\n    }\r\n\r\n    if (onSubmit == null) {\r\n      return;\r\n    }\r\n\r\n    this.setState({submitting: true});\r\n\r\n    const result = await onSubmit(formData);\r\n    \r\n    if (!mounted) {\r\n      return;\r\n    }\r\n\r\n    if (result) {\r\n      this.updateRemoteErrors(result);\r\n    }\r\n\r\n    this.setState({submitting: false});\r\n  }\r\n```\r\n\r\n_Surely there's a more elegant way to implement what I'm proposing._ 😅 \r\n\r\nI'd love to hear your thoughts on this! And alternative solutions to this problem, of course. I'm happy to send a PR if you're okay with it. 🙏 Hopefully the problem makes sense, I struggled a while until I found what was going on."}}
{"actor":{"display_login":"michelleyschen"}}