From d56aec2dd13282acc31f2e9c9dc86660d6284444 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 23 Sep 2022 22:58:50 +0100 Subject: [PATCH] [Beta] Remove inline useEvent impl (#5101) --- beta/src/content/learn/escape-hatches.md | 25 +- .../learn/removing-effect-dependencies.md | 124 +++----- .../learn/reusing-logic-with-custom-hooks.md | 137 ++++----- .../learn/separating-events-from-effects.md | 269 ++++++------------ 4 files changed, 179 insertions(+), 376 deletions(-) diff --git a/beta/src/content/learn/escape-hatches.md b/beta/src/content/learn/escape-hatches.md index d98360ad..5e01c5c8 100644 --- a/beta/src/content/learn/escape-hatches.md +++ b/beta/src/content/learn/escape-hatches.md @@ -455,8 +455,8 @@ This is not ideal. You want to re-connect to the chat only if the `roomId` has c ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -471,7 +471,7 @@ This is not ideal. You want to re-connect to the chat only if the `roomId` has c ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -575,25 +575,6 @@ export function showNotification(message, theme) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label { display: block; margin-top: 10px; } ``` diff --git a/beta/src/content/learn/removing-effect-dependencies.md b/beta/src/content/learn/removing-effect-dependencies.md index dbb70308..27c4aa44 100644 --- a/beta/src/content/learn/removing-effect-dependencies.md +++ b/beta/src/content/learn/removing-effect-dependencies.md @@ -1253,10 +1253,26 @@ Is there a line of code inside the Effect that should not be reactive? How can y +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + ```js import { useState, useEffect, useRef } from 'react'; +import { experimental_useEvent as useEvent } from 'react'; import { FadeInAnimation } from './animation.js'; -import { useEvent } from './useEvent.js'; function Welcome({ duration }) { const ref = useRef(null); @@ -1351,25 +1367,6 @@ export class FadeInAnimation { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label, button { display: block; margin-bottom: 20px; } html, body { min-height: 300px; } @@ -1383,10 +1380,26 @@ Your Effect needs to read the latest value of `duration`, but you don't want it +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + ```js import { useState, useEffect, useRef } from 'react'; import { FadeInAnimation } from './animation.js'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; function Welcome({ duration }) { const ref = useRef(null); @@ -1479,25 +1492,6 @@ export class FadeInAnimation { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label, button { display: block; margin-bottom: 20px; } html, body { min-height: 300px; } @@ -1825,8 +1819,8 @@ Another of these functions only exists to pass some state to an imported API met ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1907,7 +1901,7 @@ export default function App() { ```js ChatRoom.js active import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export default function ChatRoom({ roomId, createConnection, onMessage }) { useEffect(() => { @@ -2023,25 +2017,6 @@ export function showNotification(message, theme) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label, button { display: block; margin-bottom: 5px; } ``` @@ -2139,8 +2114,8 @@ As a result, the chat re-connects only when something meaningful (`roomId` or `i ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -2208,7 +2183,7 @@ export default function App() { ```js ChatRoom.js active import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; import { createEncryptedConnection, createUnencryptedConnection, @@ -2342,25 +2317,6 @@ export function showNotification(message, theme) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label, button { display: block; margin-bottom: 5px; } ``` diff --git a/beta/src/content/learn/reusing-logic-with-custom-hooks.md b/beta/src/content/learn/reusing-logic-with-custom-hooks.md index a2e4d5cd..134e1509 100644 --- a/beta/src/content/learn/reusing-logic-with-custom-hooks.md +++ b/beta/src/content/learn/reusing-logic-with-custom-hooks.md @@ -985,7 +985,7 @@ export default function ChatRoom({ roomId }) { ```js useChatRoom.js import { useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; import { createConnection } from './chat.js'; export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { @@ -1006,25 +1006,6 @@ export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```js chat.js export function createConnection({ serverUrl, roomId }) { // A real implementation would actually connect to the server @@ -1089,8 +1070,8 @@ export function showNotification(message, theme = 'dark') { ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1660,7 +1641,7 @@ export default function App() { ```js useFadeIn.js active import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export function useFadeIn(ref, duration) { const [isRunning, setIsRunning] = useState(true); @@ -1696,25 +1677,6 @@ function useAnimationLoop(isRunning, drawFrame) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label, button { display: block; margin-bottom: 20px; } html, body { min-height: 300px; } @@ -1728,6 +1690,22 @@ html, body { min-height: 300px; } } ``` +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + However, you didn't *have to* do that. As with regular functions, ultimately you decide where to draw the boundaries between different parts of your code. For example, you could also take a very different approach. Instead of keeping the logic in the Effect, you could move most of the imperative logic inside a JavaScript [class:](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) @@ -2202,6 +2180,22 @@ It looks like your `useInterval` Hook accepts an event listener as an argument. +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + ```js import { useCounter } from './useCounter.js'; import { useInterval } from './useInterval.js'; @@ -2233,7 +2227,7 @@ export function useCounter(delay) { ```js useInterval.js import { useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export function useInterval(onTick, delay) { useEffect(() => { @@ -2245,25 +2239,6 @@ export function useInterval(onTick, delay) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - @@ -2276,6 +2251,23 @@ With this change, both intervals work as expected and don't interfere with each +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + + ```js import { useCounter } from './useCounter.js'; import { useInterval } from './useInterval.js'; @@ -2307,7 +2299,7 @@ export function useCounter(delay) { ```js useInterval.js active import { useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export function useInterval(callback, delay) { const onTick = useEvent(callback); @@ -2318,25 +2310,6 @@ export function useInterval(callback, delay) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - diff --git a/beta/src/content/learn/separating-events-from-effects.md b/beta/src/content/learn/separating-events-from-effects.md index 2d99036e..6d8881a5 100644 --- a/beta/src/content/learn/separating-events-from-effects.md +++ b/beta/src/content/learn/separating-events-from-effects.md @@ -448,8 +448,8 @@ Verify that the new behavior works as you would expect: ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -464,7 +464,7 @@ Verify that the new behavior works as you would expect: ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -568,25 +568,6 @@ export function showNotification(message, theme) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label { display: block; margin-top: 10px; } ``` @@ -817,7 +798,7 @@ With `useEvent`, there is no need to "lie" to the linter, and the code works as ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export default function App() { const [position, setPosition] = useState({ x: 0, y: 0 }); @@ -861,25 +842,6 @@ export default function App() { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css body { height: 200px; @@ -1106,9 +1068,25 @@ It seems like the Effect which sets up the timer "reacts" to the `increment` val +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1145,25 +1123,6 @@ export default function Timer() { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css button { margin: 10px; } ``` @@ -1178,9 +1137,25 @@ To solve the issue, extract an `onTick` Event function from the Effect: +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1222,25 +1197,6 @@ export default function Timer() { ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css button { margin: 10px; } ``` @@ -1263,9 +1219,25 @@ Code inside Event functions is not reactive. Are there cases in which you would +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1322,25 +1294,6 @@ export default function Timer() { ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css button { margin: 10px; } ``` @@ -1353,9 +1306,25 @@ The problem with the above example is that it extracted an Event function called +```json package.json hidden +{ + "dependencies": { + "react": "experimental", + "react-dom": "experimental", + "react-scripts": "latest" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} +``` + ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; export default function Timer() { const [count, setCount] = useState(0); @@ -1407,25 +1376,6 @@ export default function Timer() { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css button { margin: 10px; } ``` @@ -1455,8 +1405,8 @@ Your Effect knows which room it connected to. Is there any information that you ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1471,7 +1421,7 @@ Your Effect knows which room it connected to. Is there any information that you ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -1577,25 +1527,6 @@ export function showNotification(message, theme) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label { display: block; margin-top: 10px; } ``` @@ -1615,8 +1546,8 @@ To fix the issue, instead of reading the *latest* `roomId` inside the Event func ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1631,7 +1562,7 @@ To fix the issue, instead of reading the *latest* `roomId` inside the Event func ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -1737,25 +1668,6 @@ export function showNotification(message, theme) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label { display: block; margin-top: 10px; } ``` @@ -1771,8 +1683,8 @@ To solve the additional challenge, save the notification timeout ID and clear it ```json package.json hidden { "dependencies": { - "react": "latest", - "react-dom": "latest", + "react": "experimental", + "react-dom": "experimental", "react-scripts": "latest", "toastify-js": "1.12.0" }, @@ -1787,7 +1699,7 @@ To solve the additional challenge, save the notification timeout ID and clear it ```js import { useState, useEffect } from 'react'; -import { useEvent } from './useEvent.js'; +import { experimental_useEvent as useEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; @@ -1899,25 +1811,6 @@ export function showNotification(message, theme) { } ``` -```js useEvent.js -import { useRef, useInsertionEffect, useCallback } from 'react'; - -// The useEvent API has not yet been added to React, -// so this is a temporary shim to make this sandbox work. -// You're not expected to write code like this yourself. - -export function useEvent(fn) { - const ref = useRef(null); - useInsertionEffect(() => { - ref.current = fn; - }, [fn]); - return useCallback((...args) => { - const f = ref.current; - return f(...args); - }, []); -} -``` - ```css label { display: block; margin-top: 10px; } ```