Skip to content

Commit 70d2216

Browse files
authored
fix TypeError: Cannot read properties of null (reading 'get') and update graphiql webpack example to show how to use useStorage hook with GraphiQL component (#3993)
* upd * upd
1 parent 4b84ea5 commit 70d2216

File tree

6 files changed

+48
-30
lines changed

6 files changed

+48
-30
lines changed

.changeset/stale-rice-camp.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'graphiql': patch
3+
---
4+
5+
fix `TypeError: Cannot read properties of null (reading 'get')` and update graphiql webpack example to show how to use `useStorage` hook with `GraphiQL` component

examples/graphiql-webpack/src/constants.js

Lines changed: 0 additions & 6 deletions
This file was deleted.

examples/graphiql-webpack/src/index.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
@import 'graphiql/style.css';
2+
@import '@graphiql/plugin-explorer/style.css';
3+
@import '@graphiql/plugin-code-exporter/style.css';
4+
@import './select-server-plugin.css';
5+
16
body {
27
padding: 0;
38
margin: 0;

examples/graphiql-webpack/src/index.jsx

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,26 @@
11
import 'regenerator-runtime/runtime.js';
2-
import * as React from 'react';
2+
import React, { useState, useEffect, useMemo } from 'react';
33
import { createRoot } from 'react-dom/client';
44
import { GraphiQL } from 'graphiql';
55
import { explorerPlugin } from '@graphiql/plugin-explorer';
66
import { getSnippets } from './snippets';
77
import { codeExporterPlugin } from '@graphiql/plugin-code-exporter';
8-
import 'graphiql/style.css';
9-
import '@graphiql/plugin-explorer/style.css';
10-
import '@graphiql/plugin-code-exporter/style.css';
118
import { createGraphiQLFetcher } from '@graphiql/toolkit';
129
import { useStorage } from '@graphiql/react';
13-
14-
export const STARTING_URL =
15-
'https://swapi-graphql.netlify.app/.netlify/functions/index';
16-
17-
import './index.css';
1810
import { serverSelectPlugin, LAST_URL_KEY } from './select-server-plugin';
11+
import './index.css';
12+
13+
export const STARTING_URL = 'https://countries.trevorblades.com';
1914

2015
if ('serviceWorker' in navigator) {
2116
window.addEventListener('load', () => {
2217
navigator.serviceWorker
2318
.register('/service-worker.js')
2419
.then(registration => {
25-
console.log('SW registered: ', registration);
20+
console.log('SW registered:', registration);
2621
})
2722
.catch(registrationError => {
28-
console.log('SW registration failed: ', registrationError);
23+
console.error('SW registration failed:', registrationError);
2924
});
3025
});
3126
}
@@ -59,24 +54,21 @@ const style = { height: '100vh' };
5954
*/
6055
const explorer = explorerPlugin();
6156

62-
const App = () => {
63-
// TODO: `storage` will be always `null`, fix it to have access outside `StorageContextProvider` after zustand migration
64-
const storage = useStorage();
65-
const lastUrl = storage?.get(LAST_URL_KEY);
66-
const [currentUrl, setUrl] = React.useState(lastUrl ?? STARTING_URL);
57+
function App() {
58+
const [currentUrl, setUrl] = useState('');
6759
// TODO: a breaking change where we make URL an internal state concern, and then expose hooks
6860
// so that you can handle/set URL state internally from a plugin
6961
// fetcher could then pass a dynamic URL config object to the fetcher internally
70-
const exporter = React.useMemo(
62+
const exporter = useMemo(
7163
() =>
7264
codeExporterPlugin({ snippets: getSnippets({ serverUrl: currentUrl }) }),
7365
[currentUrl],
7466
);
75-
const fetcher = React.useMemo(
67+
const fetcher = useMemo(
7668
() => createGraphiQLFetcher({ url: currentUrl }),
7769
[currentUrl],
7870
);
79-
const serverSelect = React.useMemo(
71+
const serverSelect = useMemo(
8072
() => serverSelectPlugin({ url: currentUrl, setUrl }),
8173
[currentUrl],
8274
);
@@ -87,9 +79,26 @@ const App = () => {
8779
plugins={[serverSelect, explorer, exporter]}
8880
fetcher={fetcher}
8981
shouldPersistHeaders
90-
/>
82+
>
83+
<GraphiQLStorageBound setUrl={setUrl} />
84+
</GraphiQL>
9185
);
92-
};
86+
}
87+
88+
/**
89+
* `useStorage` is a context hook that's only available within the `<GraphiQL>`
90+
* provider tree. `<GraphiQLStorageBound>` must be rendered as a child of `<GraphiQL>`.
91+
*/
92+
function GraphiQLStorageBound({ setUrl }) {
93+
const storage = useStorage();
94+
const lastUrl = storage.get(LAST_URL_KEY) ?? STARTING_URL;
95+
96+
useEffect(() => {
97+
setUrl(lastUrl);
98+
}, [lastUrl, setUrl]);
99+
100+
return null;
101+
}
93102

94103
const root = createRoot(document.getElementById('root'));
95104
root.render(<App />);

examples/graphiql-webpack/src/select-server-plugin.jsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import * as React from 'react';
2-
3-
import './select-server-plugin.css';
42
import { useStorage, useSchemaStore } from '@graphiql/react';
53

64
export const LAST_URL_KEY = 'lastURL';

packages/graphiql/src/GraphiQL.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,13 @@ export const GraphiQLInterface: FC<GraphiQLInterfaceProps> = props => {
316316
'success' | 'error' | null
317317
>(null);
318318

319-
const { logo, toolbar, footer } = Children.toArray(props.children).reduce<{
319+
const { logo, toolbar, footer, children } = Children.toArray(
320+
props.children,
321+
).reduce<{
320322
logo?: ReactNode;
321323
toolbar?: ReactNode;
322324
footer?: ReactNode;
325+
children: ReactNode[];
323326
}>(
324327
(acc, curr) => {
325328
switch (getChildComponentType(curr)) {
@@ -336,6 +339,8 @@ export const GraphiQLInterface: FC<GraphiQLInterfaceProps> = props => {
336339
case GraphiQL.Footer:
337340
acc.footer = curr;
338341
break;
342+
default:
343+
acc.children.push(curr);
339344
}
340345
return acc;
341346
},
@@ -348,6 +353,7 @@ export const GraphiQLInterface: FC<GraphiQLInterfaceProps> = props => {
348353
onPrettifyQuery={props.onPrettifyQuery}
349354
/>
350355
),
356+
children: [],
351357
},
352358
);
353359

@@ -823,6 +829,7 @@ export const GraphiQLInterface: FC<GraphiQLInterfaceProps> = props => {
823829
</div>
824830
</Dialog>
825831
</div>
832+
{children}
826833
</Tooltip.Provider>
827834
);
828835
};

0 commit comments

Comments
 (0)