{"action":"created","issue":{"url":"https://api.github.com/repos/idibidiart/react-native-responsive-grid/issues/24","repository_url":"https://api.github.com/repos/idibidiart/react-native-responsive-grid","labels_url":"https://api.github.com/repos/idibidiart/react-native-responsive-grid/issues/24/labels{/name}","comments_url":"https://api.github.com/repos/idibidiart/react-native-responsive-grid/issues/24/comments","events_url":"https://api.github.com/repos/idibidiart/react-native-responsive-grid/issues/24/events","html_url":"https://github.com/idibidiart/react-native-responsive-grid/issues/24","id":312403395,"number":24,"title":"React 16.x causes exception with setState on unmounted component","user":{"login":"peacechen","id":6295083,"avatar_url":"https://avatars3.githubusercontent.com/u/6295083?v=4","gravatar_id":"","url":"https://api.github.com/users/peacechen","html_url":"https://github.com/peacechen","followers_url":"https://api.github.com/users/peacechen/followers","following_url":"https://api.github.com/users/peacechen/following{/other_user}","gists_url":"https://api.github.com/users/peacechen/gists{/gist_id}","starred_url":"https://api.github.com/users/peacechen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/peacechen/subscriptions","organizations_url":"https://api.github.com/users/peacechen/orgs","repos_url":"https://api.github.com/users/peacechen/repos","events_url":"https://api.github.com/users/peacechen/events{/privacy}","received_events_url":"https://api.github.com/users/peacechen/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":19,"created_at":"2018-04-09T05:58:28Z","updated_at":"2018-04-10T16:56:40Z","closed_at":"2018-04-10T16:56:40Z","author_association":"CONTRIBUTOR","body":"Changes in React 16.x lifecycle can result in an exception during unmount.  `<Grid>` calls `cancelAnimationFrame` in `componentWillUnmount`, however there is a race condition with either `runAfterInteractions` or `requestAnimationFrame` that allow setState() to run even though it was attempted to be canceled.\r\n\r\nI'm testing a fix which sets an `unmounted` flag in `componentWillUnmount`.  The callback checks for the flag and bails if it's set, closing the loop on the race condition.\r\n\r\n```\r\nCan'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    in Grid (at myComponent.js:...)\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/idibidiart/react-native-responsive-grid/issues/comments/380173188","html_url":"https://github.com/idibidiart/react-native-responsive-grid/issues/24#issuecomment-380173188","issue_url":"https://api.github.com/repos/idibidiart/react-native-responsive-grid/issues/24","id":380173188,"user":{"login":"idibidiart","id":1288823,"avatar_url":"https://avatars3.githubusercontent.com/u/1288823?v=4","gravatar_id":"","url":"https://api.github.com/users/idibidiart","html_url":"https://github.com/idibidiart","followers_url":"https://api.github.com/users/idibidiart/followers","following_url":"https://api.github.com/users/idibidiart/following{/other_user}","gists_url":"https://api.github.com/users/idibidiart/gists{/gist_id}","starred_url":"https://api.github.com/users/idibidiart/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/idibidiart/subscriptions","organizations_url":"https://api.github.com/users/idibidiart/orgs","repos_url":"https://api.github.com/users/idibidiart/repos","events_url":"https://api.github.com/users/idibidiart/events{/privacy}","received_events_url":"https://api.github.com/users/idibidiart/received_events","type":"User","site_admin":false},"created_at":"2018-04-10T16:56:40Z","updated_at":"2018-04-10T16:56:40Z","author_association":"OWNER","body":"Published re-fix with comments\r\n\r\n```\r\nexport default class Grid extends React.Component {\r\n    constructor(props) {\r\n        super(props)\r\n        this.state = {...props.state, layout: {}}\r\n        this.animFrame\r\n        this.unmounting = false\r\n    }\r\n\r\n    componentWillUnmount = () => {\r\n        this.unmounting = true\r\n        cancelAnimationFrame(this.animFrame)\r\n    }\r\n\r\n    callback = (e) => {\r\n\r\n        // callback to runAfterInteractions is async\r\n        // so onLayout might be triggered before component is unmounted\r\n        // and it mifght schedule rFA after component is unmounted \r\n        // so cAF in componentWillUnmount would then miss that rFA\r\n        if (this.unmounting) return\r\n\r\n        const layout = {\r\n          screen: ScreenInfo(), \r\n          grid: e.nativeEvent.layout\r\n        }\r\n        this.setState((state) => {\r\n            return {...state, layout}\r\n        })\r\n    }\r\n\r\n    render() {\r\n        return (\r\n            <View\r\n                style={[\r\n                    {\r\n                        flex: 1\r\n                    },\r\n                    this.props.style\r\n                ]}\r\n                \r\n                onLayout={(e) => {\r\n                    e.persist()\r\n                    InteractionManager.runAfterInteractions(() => {\r\n                        // avoid queuing up rFA tasks\r\n                        cancelAnimationFrame(this.animFrame)\r\n                        this.animFrame = requestAnimationFrame(() => {\r\n                            this.callback(e)\r\n                        })\r\n                    })\r\n                }}\r\n            >\r\n                {this.props.children({\r\n                    state: this.state,\r\n                    setState: (...args) => this.setState(...args),\r\n                })}\r\n            </View>)\r\n    }\r\n}\r\n```"}}
{"actor":{"display_login":"idibidiart"}}