diff options
author | Kero van Gelder <keroami@users.noreply.github.com> | 2023-04-05 14:33:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-05 13:33:00 +0100 |
commit | a1b2f0344a59e24ae4d16f1689a02ba72ced2b53 (patch) | |
tree | 83843c6afd048b2c770a811696e1947bf915ffaa /src | |
parent | 2feb2a8e1a427cb7545785e2678e4523533c432e (diff) | |
download | lustre-a1b2f0344a59e24ae4d16f1689a02ba72ced2b53.tar.gz lustre-a1b2f0344a59e24ae4d16f1689a02ba72ced2b53.zip |
🐛 Avoid a race condition when dispatch is used twice from outside (#8)
* Upgrade examples to lustre 2.0
* Upgrade to gleam 0.27.0
* Fix: avoid a race condition when dispatch is used twice from outside
E.g. websocket open + websocket msg arriving
Application returns a Cmd from update(model, msg)
but the Effect handler is *not* ran be React 18 before
the websocket msg-arriving is handled by the Reducer.
Result: The Cmd returned by update(model, msg) is dropped silently.
---------
Co-authored-by: Kero van Gelder <kero@chmeee.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/ffi.mjs | 29 |
1 files changed, 23 insertions, 6 deletions
diff --git a/src/ffi.mjs b/src/ffi.mjs index cb458d4..b6883d4 100644 --- a/src/ffi.mjs +++ b/src/ffi.mjs @@ -35,20 +35,37 @@ export const mount = ({ init, update, render }, selector) => { // let dispatch = null; + let init_cmds = null; + const App = React.createElement(() => { - const [[state, cmds], $dispatch] = React.useReducer( - ([state, _], action) => update(state, action), - init + const [state, $dispatch] = React.useReducer( + (state, action) => { + let [new_state, cmds] = update(state, action) + // Handle cmds immediately, they're not React-kind-of-state + for (const cmd of Cmd.to_list(cmds)) { + cmd(dispatch); + } + return new_state + }, + undefined, + () => { + let [state, cmds] = init + // postpone handling cmds, as we do not have the dispatch, yet + init_cmds = cmds + return state + } ); - const el = render(state); if (dispatch === null) dispatch = $dispatch; + const el = render(state); + React.useEffect(() => { - for (const cmd of Cmd.to_list(cmds)) { + for (const cmd of Cmd.to_list(init_cmds)) { cmd($dispatch); } - }, [cmds]); + init_cmds = undefined; // Just so we get an error, rather than re-execute + }, []); // empty deps array means this effect will only run on initial render return typeof el == "string" ? el : el($dispatch); }); |