From 59f163adaffc316215cfdf2e16e06887ef188c62 Mon Sep 17 00:00:00 2001
From: Danry Ague <74456102+decanTyme@users.noreply.github.com>
Date: Sat, 9 Apr 2022 07:40:22 +0800
Subject: [PATCH] feat!: Migrate to `wrapPageElement` Gatsby API
* Rewrite `ReplaceComponentRenderer` to functional component
BREAKING CHANGE: Removes support for `replaceComponentRenderer` API from Gatsby v2.
---
gatsby-browser.js | 6 +-
src/ModalRoutingContext.js | 6 +-
src/PageElementWrapper.jsx | 133 +++++++++++++++++++++++++++++
src/onClientEntry.js | 2 +-
src/replaceComponentRenderer.js | 146 --------------------------------
5 files changed, 138 insertions(+), 155 deletions(-)
create mode 100644 src/PageElementWrapper.jsx
delete mode 100644 src/replaceComponentRenderer.js
diff --git a/gatsby-browser.js b/gatsby-browser.js
index 8a5d249..aab35b5 100644
--- a/gatsby-browser.js
+++ b/gatsby-browser.js
@@ -1,9 +1,7 @@
-const {
- default: replaceComponentRenderer,
-} = require('./dist/replaceComponentRenderer')
+const { default: wrapPageElement } = require('./dist/PageElementWrapper')
const { default: shouldUpdateScroll } = require('./dist/shouldUpdateScroll')
const { default: onClientEntry } = require('./dist/onClientEntry')
exports.onClientEntry = onClientEntry
-exports.replaceComponentRenderer = replaceComponentRenderer
+exports.wrapPageElement = wrapPageElement
exports.shouldUpdateScroll = shouldUpdateScroll
diff --git a/src/ModalRoutingContext.js b/src/ModalRoutingContext.js
index a32ad59..940ca47 100644
--- a/src/ModalRoutingContext.js
+++ b/src/ModalRoutingContext.js
@@ -1,10 +1,8 @@
import React from 'react'
-export const defaultValue = {
+const ModalRoutingContext = React.createContext({
isModal: false,
closeTo: null,
-}
-
-const ModalRoutingContext = React.createContext(defaultValue)
+})
export default ModalRoutingContext
diff --git a/src/PageElementWrapper.jsx b/src/PageElementWrapper.jsx
new file mode 100644
index 0000000..025e6df
--- /dev/null
+++ b/src/PageElementWrapper.jsx
@@ -0,0 +1,133 @@
+import { navigate } from 'gatsby'
+import React from 'react'
+import Modal from 'react-modal'
+import ModalRoutingContext from './ModalRoutingContext'
+
+const withoutPrefix = (path) => {
+ const prefix =
+ typeof __BASE_PATH__ !== `undefined` ? __BASE_PATH__ : __PATH_PREFIX__
+
+ return path.slice(prefix ? prefix.length : 0)
+}
+
+const element = {}
+
+function PageElementWrapper({ props, opts }) {
+ const modalContentRef = React.useRef(null)
+ const [state, setState] = React.useState({
+ prevProps: null,
+ lastModalProps: null,
+ props: null,
+ pathname: null,
+ })
+
+ const handleRequestClose = () => {
+ navigate(withoutPrefix(state.prevProps.location.pathname), {
+ state: {
+ noScroll: true,
+ },
+ })
+ }
+
+ React.useEffect(() => {
+ if (
+ state.prevProps?.location.pathname !== props.location.pathname &&
+ props.location.state.modal &&
+ modalContentRef.current
+ ) {
+ modalContentRef.current.scrollTop = 0
+ }
+ })
+
+ if (state.pathname !== props?.location.pathname) {
+ setState({
+ pathname: props?.location.pathname,
+ props,
+ ...(state.props?.location.state.modal
+ ? {
+ // Old page was a modal, keep track so we
+ // can render the contents while closing
+ lastModalProps: state.props,
+ }
+ : {
+ // Old page was not a modal, keep track so
+ // we can render the contents under modals
+ prevProps: state.props,
+ }),
+ })
+ }
+
+ const isModal = state.prevProps && props.location.state.modal
+
+ const resources = isModal
+ ? state.prevProps.pageResources
+ : props.pageResources
+
+ // The page is the previous path if this is a modal,
+ // otherwise it's the current path
+ element.page = React.createElement(resources.component, {
+ ...(isModal ? state.prevProps : props),
+ key: resources.page.path,
+ })
+
+ if (isModal) {
+ // Rendering the current page as a modal,
+ // so create an element with the page contents
+ element.modal = React.createElement(props.pageResources.component, {
+ ...props,
+ key: props?.pageResources.page.path,
+ })
+ } else if (state.lastModalProps) {
+ // Not rendering the current page as a modal, but we may
+ // be in the process of animating the old modal content
+ // to close, so render the last modal content we have cached
+ element.modal = React.createElement(
+ state.lastModalProps.pageResources.component,
+ {
+ ...state.lastModalProps,
+ key: state.lastModalProps.pageResources.page.path,
+ }
+ )
+ }
+
+ const modalValues = React.useMemo(
+ () => ({
+ modal: isModal,
+ closeTo: state.prevProps
+ ? withoutPrefix(state.prevProps.location.pathname)
+ : '/',
+ }),
+ [isModal, state.prevProps]
+ )
+
+ return (
+ <>
+ {element.page}
+
+ {
+ modalContentRef.current = node
+ }}
+ {...opts?.modalProps}
+ isOpen={Boolean(isModal)}
+ >
+ {element.modal ? (
+
+
+ {element.modal}
+
+
+ ) : null}
+
+ >
+ )
+}
+
+const wrapPageElement = (context, opts) =>
+ React.createElement(PageElementWrapper, {
+ ...context,
+ opts,
+ })
+
+export default wrapPageElement
diff --git a/src/onClientEntry.js b/src/onClientEntry.js
index e9adb7c..8a1d051 100644
--- a/src/onClientEntry.js
+++ b/src/onClientEntry.js
@@ -1,6 +1,6 @@
import Modal from 'react-modal'
-const onClientEntry = (_args, opts = {}) => {
+const onClientEntry = (_, opts) => {
const { appElement = `#___gatsby` } = opts
Modal.setAppElement(appElement)
}
diff --git a/src/replaceComponentRenderer.js b/src/replaceComponentRenderer.js
deleted file mode 100644
index 5174926..0000000
--- a/src/replaceComponentRenderer.js
+++ /dev/null
@@ -1,146 +0,0 @@
-import _ from 'lodash'
-import { navigate } from 'gatsby'
-import React from 'react'
-import Modal from 'react-modal'
-import ModalRoutingContext from './ModalRoutingContext'
-
-const withoutPrefix = (path) => {
- const prefix =
- typeof __BASE_PATH__ !== `undefined` ? __BASE_PATH__ : __PATH_PREFIX__
-
- return path.slice(prefix ? prefix.length : 0)
-}
-
-class ReplaceComponentRenderer extends React.Component {
- state = {
- prevProps: null,
- lastModalProps: null,
- props: null,
- pathname: null,
- }
-
- modalContentRef = null
-
- constructor(...args) {
- super(...args)
- }
-
- static getDerivedStateFromProps(props, state) {
- // TODO: handle history changes
- if (props.location.pathname !== state.pathname) {
- return {
- pathname: props.location.pathname,
- props: props,
- ...(_.get(state, 'props.location.state.modal')
- ? {
- // old page was a modal, keep track so we can render the contents while closing
- lastModalProps: state.props,
- }
- : {
- // old page was not a modal, keep track so we can render the contents under modals
- prevProps: state.props,
- }),
- }
- }
-
- return null
- }
-
- componentDidUpdate(prevProps) {
- if (
- _.get(prevProps, 'location.pathname') !==
- _.get(this.props, 'location.pathname') &&
- _.get(this.props, 'location.state.modal') &&
- this.modalContentRef
- ) {
- this.modalContentRef.scrollTop = 0
- }
- }
-
- handleRequestClose = () => {
- navigate(withoutPrefix(this.state.prevProps.location.pathname), {
- state: {
- noScroll: true,
- },
- })
- }
-
- render() {
- // render modal if props location has modal
- const { pageResources, location, modalProps } = this.props
- const { prevProps, lastModalProps } = this.state
- const isModal = prevProps && _.get(location, 'state.modal')
-
- const resources = isModal ? prevProps.pageResources : pageResources
-
- // the page is the previous path if this is a modal, otherwise it's the current path
- const pageElement = isModal
- ? React.createElement(prevProps.pageResources.component, {
- ...prevProps,
- key: prevProps.pageResources.page.path,
- })
- : React.createElement(pageResources.component, {
- ...this.props,
- key: pageResources.page.path,
- })
-
- let modalElement = null
-
- if (isModal) {
- // Rendering the current page as a modal, so create an element with the page contents
- modalElement = React.createElement(pageResources.component, {
- ...this.props,
- key: pageResources.page.path,
- })
- } else if (lastModalProps) {
- // Not rendering the current page as a modal, but we may be in the process of animating
- // the old modal content to close, so render the last modal content we have cached
-
- modalElement = React.createElement(
- _.get(lastModalProps, 'pageResources.component'),
- {
- ...lastModalProps,
- key: _.get(lastModalProps, 'pageResources.page.path'),
- }
- )
- }
-
- return (
- <>
- {pageElement}
-
- (this.modalContentRef = node)}
- {...modalProps}
- isOpen={!!isModal}
- >
- {modalElement ? (
-
-
- {modalElement}
-
-
- ) : null}
-
- >
- )
- }
-}
-
-const replaceComponentRenderer = ({ props }, opts) => {
- const { modalProps } = opts
- return React.createElement(ReplaceComponentRenderer, {
- ...props,
- modalProps,
- })
-}
-
-export default replaceComponentRenderer