Repository: tannerlinsley/react-query (ts)

Issue:
CSP enabled DevTool show errors (insert inline style)
### Describe the bug

When CSP enabled, (Style-SRC='xxx')

the console error
```
 Refused to apply inline style because it violates the following Content Security Policy directive: "style-src 'self' 'nonce-0mihlgp7' 'report-sample'
```

### Your minimal, reproducible example

https://codesandbox.io/s/heuristic-cannon-it3v0r?file=/public/index.html

### Steps to reproduce

1. start the app
2. open the app in single window
3.  open the console and refresh

### Expected behavior

No errors or passing the nonce as params

### How often does this bug happen?

Every time

### Screenshots or Videos

_No response_

### Platform

Browsers

### react-query version

3.34.19

### TypeScript version

_No response_

### Additional context

_No response_

Pull request description:
nonce for devtools style
* pass nonce to devtools
* __webpack__nonce__
* set style scoped 

fix #3495 

Patch:
diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js
index 6485faeaa..6554a0178 100644
--- a/docs/src/pages/index.js
+++ b/docs/src/pages/index.js
@@ -77,7 +77,7 @@ const Home = ({ sponsors }) => {
                         href="https://ui.dev/react-query?from=tanstack"
                         className="text-blue-600 font-semibold transition-colors duration-150 ease-out"
                       >
-                        Take the offical course
+                        Take the official course
                       </a>
                     </p>
                   </div>
diff --git a/package.json b/package.json
index 560a5dcec..9215185f2 100644
--- a/package.json
+++ b/package.json
@@ -70,7 +70,7 @@
     "match-sorter": "^6.0.2"
   },
   "peerDependencies": {
-    "react": "^16.8.0 || ^17.0.0"
+    "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
   },
   "peerDependenciesMeta": {
     "react-dom": {
diff --git a/src/devtools/devtools.tsx b/src/devtools/devtools.tsx
index 00a226de1..9ae4ea53a 100644
--- a/src/devtools/devtools.tsx
+++ b/src/devtools/devtools.tsx
@@ -58,6 +58,10 @@ interface DevtoolsOptions {
    * Defaults to 'aside'.
    */
   containerElement?: string | any
+  /**
+   * nonce for style element for CSP
+   */
+  styleNonce?: string
 }
 
 interface DevtoolsPanelOptions {
@@ -73,6 +77,10 @@ interface DevtoolsPanelOptions {
    * A boolean variable indicating whether the panel is open or closed
    */
   isOpen?: boolean
+  /**
+   * nonce for style element for CSP
+   */
+  styleNonce?: string
   /**
    * A function that toggles the open and close state of the panel
    */
@@ -92,6 +100,7 @@ export function ReactQueryDevtools({
   toggleButtonProps = {},
   position = 'bottom-left',
   containerElement: Container = 'aside',
+  styleNonce,
 }: DevtoolsOptions): React.ReactElement | null {
   const rootRef = React.useRef<HTMLDivElement>(null)
   const panelRef = React.useRef<HTMLDivElement>(null)
@@ -229,6 +238,7 @@ export function ReactQueryDevtools({
       <ThemeProvider theme={theme}>
         <ReactQueryDevtoolsPanel
           ref={panelRef as any}
+          styleNonce={styleNonce}
           {...otherPanelProps}
           style={{
             position: 'fixed',
@@ -375,7 +385,13 @@ export const ReactQueryDevtoolsPanel = React.forwardRef<
   HTMLDivElement,
   DevtoolsPanelOptions
 >(function ReactQueryDevtoolsPanel(props, ref): React.ReactElement {
-  const { isOpen = true, setIsOpen, handleDragStart, ...panelProps } = props
+  const {
+    isOpen = true,
+    styleNonce,
+    setIsOpen,
+    handleDragStart,
+    ...panelProps
+  } = props
 
   const queryClient = useQueryClient()
   const queryCache = queryClient.getQueryCache()
@@ -467,6 +483,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef<
         {...panelProps}
       >
         <style
+          nonce={styleNonce}
           dangerouslySetInnerHTML={{
             __html: `
             .ReactQueryDevtoolsPanel * {
@@ -723,7 +740,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef<
                 style={{
                   marginBottom: '.5em',
                   display: 'flex',
-                  alignItems: 'stretch',
+                  alignItems: 'start',
                   justifyContent: 'space-between',
                 }}
               >
diff --git a/src/react/Hydrate.tsx b/src/react/Hydrate.tsx
index 522189740..5e5893e01 100644
--- a/src/react/Hydrate.tsx
+++ b/src/react/Hydrate.tsx
@@ -23,13 +23,10 @@ export function useHydrate(state: unknown, options?: HydrateOptions) {
 export interface HydrateProps {
   state?: unknown
   options?: HydrateOptions
+  children?: React.ReactNode
 }
 
-export const Hydrate: React.FC<HydrateProps> = ({
-  children,
-  options,
-  state,
-}) => {
+export const Hydrate = ({ children, options, state }: HydrateProps) => {
   useHydrate(state, options)
-  return children as React.ReactElement<any>
+  return children as React.ReactElement
 }
diff --git a/src/react/QueryClientProvider.tsx b/src/react/QueryClientProvider.tsx
index e0ea8baa5..d5e590f7d 100644
--- a/src/react/QueryClientProvider.tsx
+++ b/src/react/QueryClientProvider.tsx
@@ -44,13 +44,14 @@ export const useQueryClient = () => {
 export interface QueryClientProviderProps {
   client: QueryClient
   contextSharing?: boolean
+  children?: React.ReactNode
 }
 
-export const QueryClientProvider: React.FC<QueryClientProviderProps> = ({
+export const QueryClientProvider = ({
   client,
   contextSharing = false,
   children,
-}) => {
+}: QueryClientProviderProps): JSX.Element => {
   React.useEffect(() => {
     client.mount()
     return () => {
diff --git a/src/react/QueryErrorResetBoundary.tsx b/src/react/QueryErrorResetBoundary.tsx
index 284de240e..b380e275b 100644
--- a/src/react/QueryErrorResetBoundary.tsx
+++ b/src/react/QueryErrorResetBoundary.tsx
@@ -38,9 +38,9 @@ export interface QueryErrorResetBoundaryProps {
     | React.ReactNode
 }
 
-export const QueryErrorResetBoundary: React.FC<QueryErrorResetBoundaryProps> = ({
+export const QueryErrorResetBoundary = ({
   children,
-}) => {
+}: QueryErrorResetBoundaryProps) => {
   const value = React.useMemo(() => createValue(), [])
   return (
     <QueryErrorResetBoundaryContext.Provider value={value}>


Test patch:
diff --git a/src/devtools/tests/devtools.test.tsx b/src/devtools/tests/devtools.test.tsx
index b529052a0..4ee9820d6 100644
--- a/src/devtools/tests/devtools.test.tsx
+++ b/src/devtools/tests/devtools.test.tsx
@@ -391,4 +391,21 @@ describe('ReactQueryDevtools', () => {
     expect(queries[1]?.textContent).toEqual(query2Hash)
     expect(queries[2]?.textContent).toEqual(query3Hash)
   })
+
+  it('style should have a nonce', async () => {
+    const { queryClient } = createQueryClient()
+
+    function Page() {
+      return <div></div>
+    }
+
+    const { container } = renderWithClient(queryClient, <Page />, {
+      styleNonce: 'test-nonce',
+      initialIsOpen: false,
+    })
+    const styleTag = container.querySelector('style')
+    expect(styleTag).toHaveAttribute('nonce', 'test-nonce')
+
+    await screen.findByRole('button', { name: /react query devtools/i })
+  })
 })
diff --git a/src/react/tests/utils.tsx b/src/react/tests/utils.tsx
index 314c3183b..26bc71ac7 100644
--- a/src/react/tests/utils.tsx
+++ b/src/react/tests/utils.tsx
@@ -67,9 +67,12 @@ export const expectType = <T,>(_: T): void => undefined
 export const expectTypeNotAny = <T,>(_: 0 extends 1 & T ? never : T): void =>
   undefined
 
-export const Blink: React.FC<{ duration: number }> = ({
+export const Blink = ({
   duration,
   children,
+}: {
+  duration: number
+  children: React.ReactNode
 }) => {
   const [shouldShow, setShouldShow] = React.useState<boolean>(true)