[bug] tauri v2.0.x 'unlisten' work strangely
Describe the bug
The following js code listens to an event in the useEffect and unlistens the event when the side effect is removed(when refreshing page).
It works fine in v1, and the haddle time will always be 1.
But in v2.x, after refreshing the page, the next listen handleTime will still be increased by 1, and all the triggered event have the same eventId. And when the browser console is open, the behavior is even strangler.
import { invoke } from "@tauri-apps/api/core";
import { useEffect, useState } from "react";
import { listen } from "@tauri-apps/api/event";
export default function App() {
const [handleTime, setHandleTime] = useState(0);
function msgHandler(msg) {
setHandleTime((prev) => prev + 1);
console.log(msg);
}
useEffect(() => {
let unlistenPromise = listen("event", msgHandler);
return async () => {
const unlisten = await unlistenPromise;
if (unlisten) {
unlisten()
.then((rst) => {
console.log("call unlisten");
})
.catch((e) => {
alert("call unlisten error: " + e);
});
}
};
}, []);
return (
<>
{/* This button invoke app.emit("event","msg") */}
<button
onClick={() => {
setHandleTime(0);
invoke("emit", "msg");
}}
>
emit
</button>
<h1>handleTime: {handleTime}</h1>
</>
);
}
Below are the backend code.
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use tauri::{generate_handler, Manager, Runtime};
#[tauri::command]
async fn emit<R: Runtime>(
app: tauri::AppHandle<R>,
_window: tauri::Window<R>,
) -> Result<(), String> {
app.emit_all("event", "msg").unwrap();
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
fn main() {
tauri::Builder::default()
.invoke_handler(generate_handler![emit])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
v1 works fine:
https://github.com/tauri-apps/tauri/assets/69547456/3f47015b-d19d-465d-b400-cff2f4214431
v2 :
https://github.com/tauri-apps/tauri/assets/69547456/dbbd2438-9cf7-40c2-a8d7-9426565d6533
https://github.com/tauri-apps/tauri/assets/69547456/f9aa2d20-d298-4b29-a8b6-19acd4d318b4
Reproduction
https://github.com/canxin121/test_listen_v1 -> works fine https://github.com/canxin121/test_listen_v2 -> works strangly
Expected behavior
The behavior of the v2.x version should be consistent with v1
Full tauri info output
for v1:
[✔] Environment
- OS: Windows 10.0.19044 X64
✔ WebView2: 117.0.2045.43
✔ MSVC:
- Visual Studio Build Tools 2019
- Visual Studio Community 2022
✔ rustc: 1.76.0-nightly (e9013ac0e 2023-12-05)
✔ cargo: 1.76.0-nightly (623b78849 2023-12-02)
✔ rustup: 1.26.0 (5af9b9484 2023-04-05)
✔ Rust toolchain: nightly-x86_64-pc-windows-msvc (default)
- node: 21.2.0
- pnpm: 8.15.2
- yarn: 1.22.21
- npm: 10.2.3
[-] Packages
- tauri [RUST]: 1.6.0
- tauri-build [RUST]: 1.5.1
- wry [RUST]: 0.24.7
- tao [RUST]: 0.16.7
- tauri-cli [RUST]: 2.0.0-beta.1
- @tauri-apps/api [NPM]: 1.5.3
- @tauri-apps/cli [NPM]: 1.5.10
[-] App
- build-type: bundle
- CSP: unset
- distDir: ../dist
- devPath: http://localhost:1420/
- framework: React
- bundler: Vite
for v2:
[✔] Environment
- OS: Windows 10.0.19044 X64
✔ WebView2: 117.0.2045.43
✔ MSVC:
- Visual Studio Build Tools 2019
- Visual Studio Community 2022
✔ rustc: 1.76.0-nightly (e9013ac0e 2023-12-05)
✔ cargo: 1.76.0-nightly (623b78849 2023-12-02)
✔ rustup: 1.26.0 (5af9b9484 2023-04-05)
✔ Rust toolchain: nightly-x86_64-pc-windows-msvc (environment override by RUSTUP_TOOLCHAIN)
- node: 21.2.0
- pnpm: 8.15.2
- yarn: 1.22.21
- npm: 10.2.3
[-] Packages
- tauri [RUST]: 2.0.0-beta.3
- tauri-build [RUST]: 2.0.0-beta.2
- wry [RUST]: 0.36.0
- tao [RUST]: 0.25.0
- tauri-cli [RUST]: 2.0.0-beta.1
- @tauri-apps/api [NPM]: 2.0.0-beta.1
- @tauri-apps/cli [NPM]: 2.0.0-beta.1
[-] App
- build-type: bundle
- CSP: unset
- frontendDist: ../dist
- devUrl: http://localhost:1420/
- framework: React
- bundler: Vite
Stack trace
In v2.x, with browser console open, got following trace.
chunk-Y2F7D3TJ.js?v=42918a2b:3 Download the React DevTools for a better development experience: https://reactjs.org/link/react-devtools
VM56:1 Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at <anonymous>:87:33
(anonymous) @ VM36:87
Promise.then(异步)
value @ VM36:94
(anonymous) @ VM38:130
action @ VM38:269
(anonymous) @ VM38:278
value @ VM38:252
invoke @ core.js:87
listen @ event.js:70
(anonymous) @ App.jsx:12
commitHookEffectListMount @ react-dom.development.js:23150
commitPassiveMountOnFiber @ react-dom.development.js:24926
commitPassiveMountEffects_complete @ react-dom.development.js:24891
commitPassiveMountEffects_begin @ react-dom.development.js:24878
commitPassiveMountEffects @ react-dom.development.js:24866
flushPassiveEffectsImpl @ react-dom.development.js:27039
flushPassiveEffects @ react-dom.development.js:26984
(anonymous) @ react-dom.development.js:26769
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
Show 13 more frames
显示简略信息
VM57:1 Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at <anonymous>:87:33
(anonymous) @ VM36:87
Promise.then(异步)
value @ VM36:94
(anonymous) @ VM38:130
action @ VM38:269
(anonymous) @ VM38:278
value @ VM38:252
invoke @ core.js:87
listen @ event.js:70
(anonymous) @ App.jsx:12
commitHookEffectListMount @ react-dom.development.js:23150
invokePassiveEffectMountInDEV @ react-dom.development.js:25154
invokeEffectsInDev @ react-dom.development.js:27351
commitDoubleInvokeEffectsInDEV @ react-dom.development.js:27330
flushPassiveEffectsImpl @ react-dom.development.js:27056
flushPassiveEffects @ react-dom.development.js:26984
(anonymous) @ react-dom.development.js:26769
workLoop @ scheduler.development.js:266
flushWork @ scheduler.development.js:239
performWorkUntilDeadline @ scheduler.development.js:533
Show 12 more frames
显示简略信息
I've located the bug, I'll try to upload a pr to fix it.
#8621
In this pr js_event_listeners are stored in the backend Listeners, while in v1 js_event_listeners are stored in the frontend.
In v1, when manually refreshing a page, all js_event_listeners on the frontend are lost to the refresh. In v2, since js_event_listeners are stored on the backend, and useEffect doesn't run the cleanup side-effects function (which sends an unlisten request to the backend) before the page is refreshed, the listener doesn't get cleaned up.
#8621
In this pr
js_event_listenersare stored in the backendListeners, while in v1js_event_listenersare stored in the frontend. In v1, when manually refreshing a page, alljs_event_listenerson the frontend are lost to the refresh. In v2, sincejs_event_listenersare stored on the backend, anduseEffectdoesn't run the cleanup side-effects function (which sends an unlisten request to the backend) before the page is refreshed, the listener doesn't get cleaned up.
this bug will be fixed in next release beta verison?
#8621 In this pr
js_event_listenersare stored in the backendListeners, while in v1js_event_listenersare stored in the frontend. In v1, when manually refreshing a page, alljs_event_listenerson the frontend are lost to the refresh. In v2, sincejs_event_listenersare stored on the backend, anduseEffectdoesn't run the cleanup side-effects function (which sends an unlisten request to the backend) before the page is refreshed, the listener doesn't get cleaned up.this bug will be fixed in next release beta verison?
It's not up to me, but I did fix the bug in pr. If you want to use the fixed tauri now, you can either switch the tauri source to my forked git link in cargo.toml, or pull it locally.
这并不由我决定,但是我已经在pr中确实的解决了这个bug. 如果现在就想使用修复后的tauri, 你可以在cargo.toml中将tauri来源切换到我的fork的git链接, 或者pull 到本地使用.