Skip to content

Commit 3d43920

Browse files
committed
Add concurrent transitions to state changes
1 parent 3e83810 commit 3d43920

File tree

5 files changed

+82
-64
lines changed

5 files changed

+82
-64
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
"@types/react": "^16.9.43",
2222
"@types/react-dom": "^16.9.8",
2323
"@types/uuid": "^8.0.0",
24+
"comlink": "^4.3.0",
2425
"concurrently": "^5.2.0",
2526
"react-scripts": "3.4.1",
26-
"typescript": "^3.9.7",
27-
"comlink": "^4.3.0"
27+
"typescript": "^3.9.7"
2828
},
2929
"scripts": {
3030
"start": "concurrently 'node server' 'react-scripts start'",

src/App.tsx

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import randomWords from "random-words";
22
import { v4 as uuid } from "uuid";
3-
import React, { useEffect, useMemo } from "react";
3+
import * as React from "react";
44
import { connectToDB, getConnection } from "./sharedb";
55
import {
66
Link,
@@ -9,6 +9,9 @@ import {
99
useLocation,
1010
} from "react-router-dom";
1111

12+
const useTransition: () => [(fn: Function) => void, boolean] = (React as any)
13+
.unstable_useTransition;
14+
1215
interface Node {
1316
text: string;
1417
}
@@ -26,16 +29,21 @@ function useFlow(config: {
2629
addNode: () => void;
2730
removeNode: (id: string) => void;
2831
reset: (flow: Flow) => void;
32+
isPending: boolean;
2933
} {
3034
// Setup
3135

36+
const [startTransition, isPending] = useTransition();
37+
3238
const [state, setState] = React.useState<Flow | null>(null);
3339

34-
const doc = useMemo(() => getConnection(config.id), [config.id]);
40+
const doc = React.useMemo(() => getConnection(config.id), [config.id]);
3541

36-
useEffect(() => {
42+
React.useEffect(() => {
3743
const cloneStateFromShareDB = () =>
38-
setState(JSON.parse(JSON.stringify(doc.data)));
44+
startTransition(() => {
45+
setState(JSON.parse(JSON.stringify(doc.data)));
46+
});
3947

4048
connectToDB(doc).then(() => {
4149
cloneStateFromShareDB();
@@ -46,7 +54,7 @@ function useFlow(config: {
4654
setState(null);
4755
doc.destroy();
4856
};
49-
}, [doc]);
57+
}, [doc, startTransition]);
5058

5159
// Methods
5260

@@ -75,6 +83,7 @@ function useFlow(config: {
7583
addNode,
7684
removeNode,
7785
reset,
86+
isPending,
7887
};
7988
}
8089

@@ -86,44 +95,47 @@ const Flow: React.FC<{ id: string }> = ({ id }) => {
8695
}
8796

8897
return (
89-
<main>
90-
<button
91-
onClick={() => {
92-
flow.addNode();
93-
}}
94-
>
95-
Add
96-
</button>
97-
<button
98-
onClick={() => {
99-
fetch("/flow.json")
100-
.then((res) => res.json())
101-
.then((flowData) => {
102-
flow.reset(flowData);
98+
<>
99+
<main>
100+
<button
101+
onClick={() => {
102+
flow.addNode();
103+
}}
104+
>
105+
Add
106+
</button>
107+
<button
108+
onClick={() => {
109+
fetch("/flow.json")
110+
.then((res) => res.json())
111+
.then((flowData) => {
112+
flow.reset(flowData);
113+
});
114+
}}
115+
>
116+
Import flow
117+
</button>
118+
<button
119+
onClick={() => {
120+
flow.reset({
121+
nodes: {},
122+
edges: [],
103123
});
104-
}}
105-
>
106-
Import flow
107-
</button>
108-
<button
109-
onClick={() => {
110-
flow.reset({
111-
nodes: {},
112-
edges: [],
113-
});
114-
}}
115-
>
116-
Reset
117-
</button>
118-
{Object.keys(flow.state.nodes).map((k) => (
119-
<NodeView
120-
key={k}
121-
onRemove={flow.removeNode}
122-
id={k}
123-
node={flow.state.nodes[k]}
124-
/>
125-
))}
126-
</main>
124+
}}
125+
>
126+
Reset
127+
</button>
128+
{Object.keys(flow.state.nodes).map((k) => (
129+
<NodeView
130+
key={k}
131+
onRemove={flow.removeNode}
132+
id={k}
133+
node={flow.state.nodes[k]}
134+
/>
135+
))}
136+
</main>
137+
{flow.isPending && <div className="overlay" />}
138+
</>
127139
);
128140
};
129141

@@ -176,7 +188,7 @@ const App = () => {
176188
}, [location]);
177189

178190
// If there is no ID readable from the hash, redirect to a freshly created one
179-
useEffect(() => {
191+
React.useEffect(() => {
180192
if (id === null) {
181193
history.push(`#${randomWords()}`);
182194
}

src/index.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import React from "react";
2-
import ReactDOM from "react-dom";
2+
import * as ReactDOM from "react-dom";
33
import App from "./App";
44
import * as serviceWorker from "./serviceWorker";
55
import "./style.css";
66

7-
ReactDOM.createRoot(
7+
(ReactDOM as any).unstable_createRoot(document.getElementById("root")).render(
88
<React.StrictMode>
99
<App />
10-
</React.StrictMode>,
11-
document.getElementById("root")
12-
).render(<App />);
10+
</React.StrictMode>
11+
);
1312

1413
// If you want your app to work offline and load faster, you can change
1514
// unregister() to register() below. Note this comes with some pitfalls.

src/style.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@ a {
3939
.remove-button:hover {
4040
background-color: #dedede;
4141
}
42+
43+
.overlay {
44+
position: fixed;
45+
top: 0;
46+
left: 0;
47+
right: 0;
48+
bottom: 0;
49+
background-color: rgba(255, 255, 255, 0.6);
50+
z-index: 1000;
51+
}

yarn.lock

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8739,14 +8739,13 @@ react-dev-utils@^10.2.1:
87398739
text-table "0.2.0"
87408740

87418741
react-dom@^0.0.0-experimental-4c8c98ab9:
8742-
version "0.0.0-fec00a869"
8743-
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-fec00a869.tgz#f952e11a31e446c00e87603998d7780bc51bdbd9"
8744-
integrity sha512-atB5i2HgCvbvhtGXq9oaX/BCL2AFZjnccougU8S9eulRFNQbNrfGNwIcj04PRo3XU1ZsBw5syL/5l596UaolKA==
8742+
version "0.0.0-experimental-4c8c98ab9"
8743+
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-experimental-4c8c98ab9.tgz#adc7613d275ac3ad03bf72c83699264a2aa235af"
8744+
integrity sha512-HOcYHzwcrm94CZ5YyZsLMNnuJCZA3uN+OTfhNJwVXUs5lXKiWcyiWCe9bwk7l0hPEYot2CI4dhQOQ4XMnCEbSQ==
87458745
dependencies:
87468746
loose-envify "^1.1.0"
87478747
object-assign "^4.1.1"
8748-
prop-types "^15.6.2"
8749-
scheduler "0.0.0-fec00a869"
8748+
scheduler "0.0.0-experimental-4c8c98ab9"
87508749

87518750
react-error-overlay@^6.0.7:
87528751
version "6.0.7"
@@ -8848,14 +8847,12 @@ [email protected]:
88488847
fsevents "2.1.2"
88498848

88508849
react@^0.0.0-experimental-4c8c98ab9:
8851-
version "0.0.0-fec00a869"
8852-
resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-fec00a869.tgz#1803f4f17cdd5adfdf614de2386c5fb5c84bbe91"
8853-
integrity sha512-FaS3ViFU4ag7cuhDHQgGK3DAdWaD8YFXzEbO/Qzz33Si7VEzRRdnyoegFwg7VkEKxR6CvCVP6revi9Tm3Gq+WQ==
8850+
version "0.0.0-experimental-4c8c98ab9"
8851+
resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-experimental-4c8c98ab9.tgz#b25742c4a9fa2b7da117738c05b29960b660ee97"
8852+
integrity sha512-pqLXmUgWtpKJD/KM5o+NbEx1UPSaW53wRBgz3XndCahOCY0TwAAxu5CEOgayvgoTIiusLhJGO9z7fD6B/wuthA==
88548853
dependencies:
88558854
loose-envify "^1.1.0"
88568855
object-assign "^4.1.1"
8857-
prop-types "^15.6.2"
8858-
scheduler "0.0.0-fec00a869"
88598856

88608857
read-pkg-up@^2.0.0:
88618858
version "2.0.0"
@@ -9355,10 +9352,10 @@ saxes@^3.1.9:
93559352
dependencies:
93569353
xmlchars "^2.1.1"
93579354

9358-
9359-
version "0.0.0-fec00a869"
9360-
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-fec00a869.tgz#e28ff1aa107e1d04d6e8901641b4fb1e3ba98577"
9361-
integrity sha512-0U25jnyBP6dRPYwaVW4WMYB0jJSYlrIHFmIuXv27X+KIHJr7vyE9gcFTqZ61NQTuxYLYepAHnUs4KgQEUDlI+g==
9355+
[email protected]experimental-4c8c98ab9:
9356+
version "0.0.0-experimental-4c8c98ab9"
9357+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-experimental-4c8c98ab9.tgz#d4384017ed8ca55dcba9b296eb1e0898d0e03ca2"
9358+
integrity sha512-32GW0cUH+xaLuFLbIa2q47p9FrHtQFCzPzFs5Ltf8jHZ1DOLNX6H6LoDdw1oKbTJf/gD4eWCtxAYeIhDs9eEwA==
93629359
dependencies:
93639360
loose-envify "^1.1.0"
93649361
object-assign "^4.1.1"

0 commit comments

Comments
 (0)