Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: compatibility with react rc 2 #35108

Merged
merged 8 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/gatsby-link/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
},
"peerDependencies": {
"@gatsbyjs/reach-router": "^1.3.5",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"homepage": "https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-link#readme",
"keywords": [
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-cxs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
"peerDependencies": {
"cxs": ">=5.0.0",
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-feed/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"main": "index.js",
"peerDependencies": {
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-fullstory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
},
"peerDependencies": {
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"engines": {
"node": ">=14.15.0"
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-google-analytics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
"main": "index.js",
"peerDependencies": {
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-google-gtag/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
"main": "index.js",
"peerDependencies": {
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-google-tagmanager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
"main": "index.js",
"peerDependencies": {
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"repository": {
"type": "git",
Expand Down
9 changes: 5 additions & 4 deletions packages/gatsby-plugin-image/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
"@types/fs-extra": "^9.0.13",
"@types/node": "^14.10.2",
"@types/prop-types": "^15.7.3",
"@types/react": "^16.9.56",
"@types/react-dom": "^16.9.8",
"@types/react": "^17.0.40",
"@types/react-dom": "^17.0.13",
"ast-pretty-print": "^2.0.1",
"babel-plugin-macros": "^2.8.0",
"cross-env": "^7.0.3",
Expand All @@ -60,6 +60,7 @@
"microbundle": "^0.13.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.2.9",
"semver": "^7.0.0",
"terser": "^5.3.8",
"typescript": "^4.5.5"
},
Expand All @@ -68,8 +69,8 @@
"gatsby": "^4.0.0-next",
"gatsby-plugin-sharp": "^4.0.0-next",
"gatsby-source-filesystem": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"dependencies": {
"@babel/code-frame": "^7.14.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
*/

import React from "react"
import { GatsbyImage, IGatsbyImageData } from "../gatsby-image.browser"
import { render, waitFor } from "@testing-library/react"
import * as hooks from "../hooks"
import type { IGatsbyImageData } from "../gatsby-image.browser"

// Prevents terser for bailing because we're not in a babel plugin
jest.mock(
Expand All @@ -20,15 +20,16 @@ jest.mock(
describe(`GatsbyImage browser`, () => {
let beforeHydrationContent: HTMLDivElement
let image: IGatsbyImageData
let GatsbyImage

beforeEach(() => {
console.warn = jest.fn()
console.error = jest.fn()
global.SERVER = true
global.GATSBY___IMAGE = true
})
global.HAS_REACT_18 = false

beforeEach(() => {
GatsbyImage = require(`../gatsby-image.browser`).GatsbyImage
image = {
width: 100,
height: 100,
Expand Down Expand Up @@ -80,6 +81,7 @@ describe(`GatsbyImage browser`, () => {
jest.clearAllMocks()
global.SERVER = undefined
global.GATSBY___IMAGE = undefined
global.HAS_REACT_18 = undefined
process.env.NODE_ENV = `test`
})

Expand Down
10 changes: 2 additions & 8 deletions packages/gatsby-plugin-image/src/components/__tests__/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { Node } from "gatsby"
import {
getSrc,
getSrcSet,
getImage,
IGatsbyImageData,
IGetImageDataArgs,
} from "../../"
import { getImageData } from "../hooks"
import { getImageData, getSrc, getSrcSet, getImage } from "../hooks"
import type { IGatsbyImageData, IGetImageDataArgs } from "../../"

const imageData: IGatsbyImageData = {
images: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* global HAS_REACT_18 */
/* eslint-disable no-unused-expressions */
import React, {
Component,
Expand All @@ -22,7 +23,33 @@ import { Layout } from "../image-utils"
import { getSizer } from "./layout-wrapper"
import { propTypes } from "./gatsby-image.server"
import { Unobserver } from "./intersection-observer"
import { render } from "react-dom"
import type { Root } from "react-dom/client"

let reactRender
if (HAS_REACT_18) {
const reactDomClient = require(`react-dom/client`)
reactRender = (
Component: React.ReactChild | Iterable<React.ReactNode>,
el: ReactDOM.Container,
root: Root
): Root => {
if (!root) {
root = reactDomClient.createRoot(el)
}

root.render(Component)

return root
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing some context here, but why is with React 18 the reactRender returning something, with older version it's void?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because Root doesn't exist in React < 18 and we need the root so we only call createRoot once.

}
} else {
const reactDomClient = require(`react-dom`)
reactRender = (
Component: React.ReactChild | Iterable<React.ReactNode>,
el: ReactDOM.Container
): void => {
reactDomClient.render(Component, el)
}
}

// eslint-disable-next-line @typescript-eslint/naming-convention
export interface GatsbyImageProps
Expand Down Expand Up @@ -69,6 +96,7 @@ class GatsbyImageHydrator extends Component<
lazyHydrator: () => void | null = null
ref = createRef<HTMLImageElement>()
unobserveRef: Unobserver
reactRootRef: MutableRefObject<Root> = createRef()

constructor(props) {
super(props)
Expand Down Expand Up @@ -108,7 +136,8 @@ class GatsbyImageHydrator extends Component<
},
this.root,
this.hydrated,
this.forceRender
this.forceRender,
this.reactRootRef
)
})
}
Expand Down Expand Up @@ -152,7 +181,11 @@ class GatsbyImageHydrator extends Component<

// // on unmount, make sure we cleanup
if (this.hydrated.current && this.lazyHydrator) {
render(null, this.root.current)
this.reactRootRef.current = reactRender(
null,
this.root.current,
this.reactRootRef.current
)
}
}

Expand Down
73 changes: 49 additions & 24 deletions packages/gatsby-plugin-image/src/components/lazy-hydrate.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/* global HAS_REACT_18 */
import React, { MutableRefObject } from "react"
import ReactDOM from "react-dom"
import { GatsbyImageProps } from "./gatsby-image.browser"
import { LayoutWrapper } from "./layout-wrapper"
import { Placeholder } from "./placeholder"
import { MainImageProps, MainImage } from "./main-image"
import { getMainProps, getPlaceholderProps } from "./hooks"
import { ReactElement } from "react"
import type { Root } from "react-dom/client"

type LazyHydrateProps = Omit<GatsbyImageProps, "as" | "style" | "className"> & {
isLoading: boolean
Expand All @@ -14,6 +15,38 @@ type LazyHydrateProps = Omit<GatsbyImageProps, "as" | "style" | "className"> & {
ref: MutableRefObject<HTMLImageElement | undefined>
}

let reactRender
let reactHydrate
if (HAS_REACT_18) {
const reactDomClient = require(`react-dom/client`)
reactRender = (
Component: React.ReactChild | Iterable<React.ReactNode>,
el: ReactDOM.Container,
root: Root
): Root => {
if (!root) {
root = reactDomClient.createRoot(el)
}

root.render(Component)

return root
}
reactHydrate = (
Component: React.ReactChild | Iterable<React.ReactNode>,
el: ReactDOM.Container
): Root => reactDomClient.hydrateRoot(el, Component)
} else {
const reactDomClient = require(`react-dom`)
reactRender = (
Component: React.ReactChild | Iterable<React.ReactNode>,
el: ReactDOM.Container
): void => {
reactDomClient.render(Component, el)
}
reactHydrate = reactDomClient.hydrate
}

export function lazyHydrate(
{
image,
Expand All @@ -31,7 +64,8 @@ export function lazyHydrate(
}: LazyHydrateProps,
root: MutableRefObject<HTMLElement | undefined>,
hydrated: MutableRefObject<boolean>,
forceHydrate: MutableRefObject<boolean>
forceHydrate: MutableRefObject<boolean>,
reactRootRef: MutableRefObject<Root>
): (() => void) | null {
const {
width,
Expand Down Expand Up @@ -87,34 +121,25 @@ export function lazyHydrate(

if (root.current) {
// Force render to mitigate "Expected server HTML to contain a matching" in develop
// @ts-ignore react 18 typings
if (ReactDOM.createRoot) {
if (!hydrated.current) {
// @ts-ignore react 18 typings
hydrated.current = ReactDOM.createRoot(root.current)
}

// @ts-ignore react 18 typings
hydrated.current.render(component)
if (hydrated.current || forceHydrate.current || HAS_REACT_18) {
reactRootRef.current = reactRender(
component,
root.current,
reactRootRef.current
)
} else {
const doRender =
hydrated.current || forceHydrate.current
? ReactDOM.render
: ReactDOM.hydrate
doRender(component, root.current)
hydrated.current = true
reactHydrate(component, root.current)
}
hydrated.current = true
}

return (): void => {
if (root.current) {
// @ts-ignore react 18 typings
if (ReactDOM.createRoot) {
// @ts-ignore react 18 typings
hydrated.current.render(null)
} else {
ReactDOM.render(null as unknown as ReactElement, root.current)
}
reactRender(
null as unknown as ReactElement,
root.current,
reactRootRef.current
)
}
}
}
4 changes: 4 additions & 0 deletions packages/gatsby-plugin-image/src/gatsby-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ImageLayoutType,
ImagePlaceholderType,
} from "./resolver-utils"
import { major } from "semver"

export * from "./node-apis/preprocess-source"

Expand Down Expand Up @@ -51,6 +52,9 @@ export const onCreateWebpackConfig: GatsbyNode["onCreateWebpackConfig"] = ({
plugins.define({
// eslint-disable-next-line @typescript-eslint/naming-convention
GATSBY___IMAGE: true,
HAS_REACT_18: JSON.stringify(
major(require(`react-dom/package.json`).version) >= 18
),
}),
],
})
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby-plugin-image/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export {}
declare global {
declare var SERVER: boolean | undefined
declare var GATSBY___IMAGE: boolean | undefined
declare var HAS_REACT_18: boolean | undefined
}
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-jss/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"main": "index.js",
"peerDependencies": {
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-mdx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"@mdx-js/mdx": "^1.0.0",
"@mdx-js/react": "^1.0.0",
"gatsby": "^4.0.0-next",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
},
"dependencies": {
"@babel/core": "^7.15.5",
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-netlify-cms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
"peerDependencies": {
"gatsby": "^4.0.0-next",
"netlify-cms-app": "^2.9.0",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0",
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0",
"webpack": "^5.0.0"
},
"repository": {
Expand Down
Loading