Skip to content

Commit f85969c

Browse files
teo-garciaJuan Mateo Garcia Ramirez
andauthored
fix: Safe setState (#1547)
Co-authored-by: Juan Mateo Garcia Ramirez <[email protected]>
1 parent 63bc78d commit f85969c

File tree

1 file changed

+23
-6
lines changed

1 file changed

+23
-6
lines changed

packages/graphiql/src/components/GraphiQL.tsx

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
156156
_storage: StorageAPI;
157157

158158
codeMirrorSizer!: CodeMirrorSizer;
159+
// Ensure the component is mounted to execute async setState
160+
componentIsMounted: boolean;
159161

160162
// refs
161163
docExplorerComponent: Maybe<DocExplorer>;
@@ -178,6 +180,9 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
178180
// Cache the storage instance
179181
this._storage = new StorageAPI(props.storage);
180182

183+
// Disable setState when the component is not mounted
184+
this.componentIsMounted = false;
185+
181186
// Determine the initial query to display.
182187
const query =
183188
props.query !== undefined
@@ -251,6 +256,9 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
251256
}
252257

253258
componentDidMount() {
259+
// Allow async state changes
260+
this.componentIsMounted = true;
261+
254262
// Only fetch schema via introspection if a schema has not been
255263
// provided, including if `null` was provided.
256264
if (this.state.schema === undefined) {
@@ -349,6 +357,9 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
349357
// When the component is about to unmount, store any persistable state, such
350358
// that when the component is remounted, it will use the last used values.
351359
componentWillUnmount() {
360+
// Deny async state changes
361+
this.componentIsMounted = false;
362+
352363
if (this.state.query) {
353364
this._storage.set('query', this.state.query);
354365
}
@@ -377,6 +388,12 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
377388
);
378389
}
379390

391+
// Use it when the state change is async
392+
// TODO: Annotate correctly this function
393+
safeSetState = (nextState: any, callback?: any): void => {
394+
this.componentIsMounted && this.setState(nextState, callback);
395+
};
396+
380397
render() {
381398
const children = React.Children.toArray(this.props.children);
382399

@@ -745,19 +762,19 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
745762
if (typeof result !== 'string' && 'data' in result) {
746763
const schema = buildClientSchema(result.data);
747764
const queryFacts = getQueryFacts(schema, this.state.query);
748-
this.setState({ schema, ...queryFacts });
765+
this.safeSetState({ schema, ...queryFacts });
749766
} else {
750767
const responseString =
751768
typeof result === 'string' ? result : GraphiQL.formatResult(result);
752-
this.setState({
769+
this.safeSetState({
753770
// Set schema to `null` to explicitly indicate that no schema exists.
754771
schema: undefined,
755772
response: responseString,
756773
});
757774
}
758775
})
759776
.catch(error => {
760-
this.setState({
777+
this.safeSetState({
761778
schema: undefined,
762779
response: error ? GraphiQL.formatError(error) : undefined,
763780
});
@@ -794,7 +811,7 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
794811
// If fetcher returned a Promise, then call the callback when the promise
795812
// resolves, otherwise handle the error.
796813
fetch.then(cb).catch(error => {
797-
this.setState({
814+
this.safeSetState({
798815
isWaitingForResponse: false,
799816
response: error ? GraphiQL.formatError(error) : undefined,
800817
});
@@ -806,14 +823,14 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
806823
const subscription = fetch.subscribe({
807824
next: cb,
808825
error: (error: Error) => {
809-
this.setState({
826+
this.safeSetState({
810827
isWaitingForResponse: false,
811828
response: error ? GraphiQL.formatError(error) : undefined,
812829
subscription: null,
813830
});
814831
},
815832
complete: () => {
816-
this.setState({
833+
this.safeSetState({
817834
isWaitingForResponse: false,
818835
subscription: null,
819836
});

0 commit comments

Comments
 (0)