-
Notifications
You must be signed in to change notification settings - Fork 47k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
394 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
EXTEND_ESLINT=true | ||
SKIP_PREFLIGHT_CHECK=true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
src/*/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
|
||
# testing | ||
/coverage | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
.env.local | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
|
||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# copies of shared | ||
src/*/shared | ||
src/*/node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"name": "react-nesting-example", | ||
"version": "0.1.0", | ||
"private": true, | ||
"dependencies": { | ||
"react-scripts": "3.4.1" | ||
}, | ||
"scripts": { | ||
"postinstall": "run-p install:*", | ||
"install:legacy": "cd src/legacy && npm install", | ||
"install:modern": "cd src/modern && npm install", | ||
"copy:legacy": "cpx 'src/shared/**' 'src/legacy/shared/'", | ||
"copy:modern": "cpx 'src/shared/**' 'src/modern/shared/'", | ||
"watch:legacy": "cpx 'src/shared/**' 'src/legacy/shared/' --watch --no-initial", | ||
"watch:modern": "cpx 'src/shared/**' 'src/modern/shared/' --watch --no-initial", | ||
"prebuild": "run-p copy:*", | ||
"prestart": "run-p copy:*", | ||
"start": "run-p start-app watch:*", | ||
"start-app": "react-scripts start", | ||
"build": "react-scripts build", | ||
"eject": "react-scripts eject" | ||
}, | ||
"eslintConfig": { | ||
"extends": "react-app" | ||
}, | ||
"browserslist": { | ||
"production": [ | ||
">0.2%", | ||
"not dead", | ||
"not op_mini all" | ||
], | ||
"development": [ | ||
"last 1 chrome version", | ||
"last 1 firefox version", | ||
"last 1 safari version" | ||
] | ||
}, | ||
"devDependencies": { | ||
"cpx": "^1.5.0", | ||
"npm-run-all": "^4.1.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<title>React App</title> | ||
</head> | ||
<body> | ||
<noscript>You need to enable JavaScript to run this app.</noscript> | ||
<div id="root"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import './modern/index'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React from 'react'; | ||
import {Component} from 'react'; | ||
import {findDOMNode} from 'react-dom'; | ||
import {Link} from 'react-router-dom'; | ||
import ThemeContext from './shared/ThemeContext'; | ||
import Clock from './shared/Clock'; | ||
|
||
export default class AboutSection extends Component { | ||
static contextType = ThemeContext; | ||
|
||
componentDidMount() { | ||
// The modern app is wrapped in StrictMode, | ||
// but the legacy bits can still use old APIs. | ||
findDOMNode(this); | ||
} | ||
|
||
render() { | ||
const theme = this.context; | ||
return ( | ||
<div style={{border: '1px dashed black', padding: 20}}> | ||
<h3>src/legacy/Greeting.js</h3> | ||
<h4 style={{color: theme}}> | ||
This component is rendered by the nested React. | ||
</h4> | ||
<Clock /> | ||
<b> | ||
<Link to="/">Go to Home</Link> | ||
</b> | ||
</div> | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* eslint-disable react/jsx-pascal-case */ | ||
|
||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import ThemeContext from './shared/ThemeContext'; | ||
|
||
// Note: this is a semi-private API, but it's ok to use it | ||
// if we never inspect the values, and only pass them through. | ||
import {__RouterContext} from 'react-router'; | ||
|
||
// Pass through every context required by this tree. | ||
// The context object is populated in src/modern/withLegacyRoot. | ||
function Bridge({children, context}) { | ||
return ( | ||
<ThemeContext.Provider value={context.theme}> | ||
<__RouterContext.Provider value={context.router}> | ||
{children} | ||
</__RouterContext.Provider> | ||
</ThemeContext.Provider> | ||
); | ||
} | ||
|
||
export default function createLegacyRoot(container) { | ||
return { | ||
render(Component, props, context) { | ||
ReactDOM.render( | ||
<Bridge context={context}> | ||
<Component {...props} /> | ||
</Bridge>, | ||
container | ||
); | ||
}, | ||
unmount() { | ||
ReactDOM.unmountComponentAtNode(container); | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"private": true, | ||
"name": "react-nesting-example-legacy", | ||
"dependencies": { | ||
"react": "0.0.0-3d0895557", | ||
"react-dom": "0.0.0-3d0895557", | ||
"react-router-dom": "5.2.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React from 'react'; | ||
import {useContext} from 'react'; | ||
import {findDOMNode} from 'react-dom'; | ||
|
||
import ThemeContext from './shared/ThemeContext'; | ||
import lazyLegacyRoot from './lazyLegacyRoot'; | ||
|
||
// Lazy-load a component from the bundle using legacy React. | ||
const Greeting = lazyLegacyRoot(() => import('../legacy/Greeting')); | ||
|
||
export default function AboutPage() { | ||
findDOMNode(); | ||
const theme = useContext(ThemeContext); | ||
return ( | ||
<> | ||
<h2>src/modern/AboutPage.js</h2> | ||
<h3 style={{color: theme}}> | ||
This component is rendered by the outer React. | ||
</h3> | ||
<Greeting /> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import React from 'react'; | ||
import {useState, Suspense} from 'react'; | ||
import {BrowserRouter, Switch, Route} from 'react-router-dom'; | ||
|
||
import HomePage from './HomePage'; | ||
import AboutPage from './AboutPage'; | ||
import ThemeContext from './shared/ThemeContext'; | ||
|
||
export default function App() { | ||
const [theme, setTheme] = useState('slategrey'); | ||
|
||
function handleToggleClick() { | ||
if (theme === 'slategrey') { | ||
setTheme('hotpink'); | ||
} else { | ||
setTheme('slategrey'); | ||
} | ||
} | ||
|
||
return ( | ||
<BrowserRouter> | ||
<ThemeContext.Provider value={theme}> | ||
<div style={{fontFamily: 'sans-serif'}}> | ||
<div | ||
style={{ | ||
margin: 20, | ||
padding: 20, | ||
border: '1px solid black', | ||
minHeight: 300, | ||
}}> | ||
<button onClick={handleToggleClick}>Toggle Theme Context</button> | ||
<br /> | ||
<Suspense fallback={<Spinner />}> | ||
<Switch> | ||
<Route path="/about"> | ||
<AboutPage /> | ||
</Route> | ||
<Route path="/"> | ||
<HomePage /> | ||
</Route> | ||
</Switch> | ||
</Suspense> | ||
</div> | ||
</div> | ||
</ThemeContext.Provider> | ||
</BrowserRouter> | ||
); | ||
} | ||
|
||
function Spinner() { | ||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
import {useContext} from 'react'; | ||
import {Link} from 'react-router-dom'; | ||
|
||
import ThemeContext from './shared/ThemeContext'; | ||
import Clock from './shared/Clock'; | ||
|
||
export default function HomePage() { | ||
const theme = useContext(ThemeContext); | ||
return ( | ||
<> | ||
<h2>src/modern/HomePage.js</h2> | ||
<h3 style={{color: theme}}> | ||
This component is rendered by the outer React. | ||
</h3> | ||
<Clock /> | ||
<b> | ||
<Link to="/about">Go to About</Link> | ||
</b> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react'; | ||
import {StrictMode} from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import App from './App'; | ||
|
||
ReactDOM.render( | ||
<StrictMode> | ||
<App /> | ||
</StrictMode>, | ||
document.getElementById('root') | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React from 'react'; | ||
import {useContext, useMemo, useRef, useState, useLayoutEffect} from 'react'; | ||
|
||
import {__RouterContext} from 'react-router'; | ||
import ThemeContext from './shared/ThemeContext'; | ||
|
||
let rendererModule = { | ||
status: 'pending', | ||
promise: null, | ||
result: null, | ||
}; | ||
|
||
export default function lazyLegacyRoot(getLegacyComponent) { | ||
let componentModule = { | ||
status: 'pending', | ||
promise: null, | ||
result: null, | ||
}; | ||
|
||
return function Wrapper(props) { | ||
const createLegacyRoot = readRecord(rendererModule, () => | ||
import('../legacy/createLegacyRoot') | ||
).default; | ||
const Component = readRecord(componentModule, getLegacyComponent).default; | ||
const containerRef = useRef(null); | ||
const [root, setRoot] = useState(null); | ||
|
||
// Popluate every contexts we want the legacy subtree to see. | ||
// Then in src/legacy/createLegacyRoot we will apply them. | ||
const theme = useContext(ThemeContext); | ||
const router = useContext(__RouterContext); | ||
const context = useMemo( | ||
() => ({ | ||
theme, | ||
router, | ||
}), | ||
[theme, router] | ||
); | ||
|
||
// Create/unmount. | ||
useLayoutEffect(() => { | ||
if (!root) { | ||
let r = createLegacyRoot(containerRef.current); | ||
setRoot(r); | ||
return () => r.unmount(); | ||
} | ||
}, [createLegacyRoot, root]); | ||
|
||
// Mout/update. | ||
useLayoutEffect(() => { | ||
if (root) { | ||
root.render(Component, props, context); | ||
} | ||
}, [Component, root, props, context]); | ||
|
||
return <div style={{display: 'contents'}} ref={containerRef} />; | ||
}; | ||
} | ||
|
||
// This is similar to React.lazy, but implemented manually. | ||
// We use this to Suspend rendering of this component until | ||
// we fetch the component and the legacy React to render it. | ||
function readRecord(record, createPromise) { | ||
if (record.status === 'fulfilled') { | ||
return record.result; | ||
} | ||
if (record.status === 'rejected') { | ||
throw record.result; | ||
} | ||
if (!record.promise) { | ||
record.promise = createPromise().then( | ||
value => { | ||
if (record.status === 'pending') { | ||
record.status = 'fulfilled'; | ||
record.promise = null; | ||
record.result = value; | ||
} | ||
}, | ||
error => { | ||
if (record.status === 'pending') { | ||
record.status = 'rejected'; | ||
record.promise = null; | ||
record.result = error; | ||
} | ||
} | ||
); | ||
} | ||
throw record.promise; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"private": true, | ||
"name": "react-nesting-example-modern", | ||
"dependencies": { | ||
"react": "0.0.0-3d0895557", | ||
"react-dom": "0.0.0-3d0895557", | ||
"react-router-dom": "5.2.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import React from 'react'; | ||
|
||
import useTime from './useTime'; | ||
|
||
export default function Clock() { | ||
const time = useTime(); | ||
return <p>Time: {time}</p>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import {createContext} from 'react'; | ||
|
||
const ThemeContext = createContext(null); | ||
|
||
export default ThemeContext; |
Oops, something went wrong.