2023-12-08 16:33:22 +03:00
|
|
|
import { baseUrl } from "./baseUrl";
|
2024-02-27 19:05:28 +03:00
|
|
|
import { SWRConfig } from "swr";
|
Replace react-tracked and react-use-websocket with useSyncExternalStore (#22386)
* refactor websockets to remove react-tracked
react 19 removed useReducer eager bailout, which broke react-tracked.
react-tracked works by wrapping state in a JavaScript Proxy. When a component reads state.someField, the proxy records that access. On the next state update, it compares only the fields each component actually touched and skips re-renders if those fields are unchanged. Under the hood, this relies on useReducer — and in React 18, useReducer had an "eager bail-out" that short-circuited rendering when the new state was === to the old state. React 19 removed that optimization, so every dispatch now schedules a render regardless, and the proxy comparison runs too late to prevent it.
useSyncExternalStore is a React primitive (added in 18, stable in 19) designed for exactly this pattern: subscribing to an external store:
useSyncExternalStore(
subscribe, // (listener) => unsubscribe — called when the store changes
getSnapshot // () => value — returns the current value for this subscriber
)
React calls getSnapshot during render and compares the result with Object.is. If the value is the same reference, the component bails out — no re-render. The key difference from react-tracked is that this bail-out is built into React's reconciler, not bolted on via proxy tricks and useReducer.
The per-topic subscription model makes this efficient. Instead of one global store where every subscriber has to check if their fields changed, each useWs("some/topic", ...) call subscribes only to that topic's listener set. When a message arrives for front_door/detect/state, only components subscribed to that exact topic get their listener fired → React calls their getSnapshot → Object.is compares the value → bail-out if unchanged. Components watching back_yard/detect/state are never even notified.
* remove react-tracked and react-use-websocket
* refactor usePolygonStates to use ws topic subscription
* fix TimeAgo refresh interval always returning 1s due to unit mismatch (seconds vs milliseconds)
older events now correctly refresh every minute/hour instead of every second
* simplify
* clean up
* don't resend onconnect
* clean up
* remove patch
2026-03-11 17:02:51 +03:00
|
|
|
import { WsProvider } from "./WsProvider";
|
2023-12-08 16:33:22 +03:00
|
|
|
import axios from "axios";
|
|
|
|
|
import { ReactNode } from "react";
|
2025-12-04 21:19:07 +03:00
|
|
|
import { isRedirectingToLogin, setRedirectingToLogin } from "./auth-redirect";
|
2023-12-08 16:33:22 +03:00
|
|
|
|
|
|
|
|
axios.defaults.baseURL = `${baseUrl}api/`;
|
|
|
|
|
|
|
|
|
|
type ApiProviderType = {
|
|
|
|
|
children?: ReactNode;
|
|
|
|
|
options?: Record<string, unknown>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function ApiProvider({ children, options }: ApiProviderType) {
|
|
|
|
|
axios.defaults.headers.common = {
|
|
|
|
|
"X-CSRF-TOKEN": 1,
|
|
|
|
|
"X-CACHE-BYPASS": 1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<SWRConfig
|
|
|
|
|
value={{
|
|
|
|
|
fetcher: (key) => {
|
|
|
|
|
const [path, params] = Array.isArray(key) ? key : [key, undefined];
|
|
|
|
|
return axios.get(path, { params }).then((res) => res.data);
|
|
|
|
|
},
|
2024-05-18 19:36:13 +03:00
|
|
|
onError: (error, _key) => {
|
2024-05-20 02:08:05 +03:00
|
|
|
if (
|
|
|
|
|
error.response &&
|
|
|
|
|
[401, 302, 307].includes(error.response.status)
|
|
|
|
|
) {
|
2024-12-10 16:42:55 +03:00
|
|
|
// redirect to the login page if not already there
|
|
|
|
|
const loginPage = error.response.headers.get("location") ?? "login";
|
2025-12-04 21:19:07 +03:00
|
|
|
if (window.location.href !== loginPage && !isRedirectingToLogin()) {
|
|
|
|
|
setRedirectingToLogin(true);
|
2024-12-10 16:42:55 +03:00
|
|
|
window.location.href = loginPage;
|
|
|
|
|
}
|
2024-05-18 19:36:13 +03:00
|
|
|
}
|
|
|
|
|
},
|
2023-12-08 16:33:22 +03:00
|
|
|
...options,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<WsWithConfig>{children}</WsWithConfig>
|
|
|
|
|
</SWRConfig>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type WsWithConfigType = {
|
|
|
|
|
children: ReactNode;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function WsWithConfig({ children }: WsWithConfigType) {
|
2024-02-27 19:05:28 +03:00
|
|
|
return <WsProvider>{children}</WsProvider>;
|
2023-12-08 16:33:22 +03:00
|
|
|
}
|
|
|
|
|
|
2024-05-18 19:36:13 +03:00
|
|
|
// eslint-disable-next-line react-refresh/only-export-components
|
2023-12-08 16:33:22 +03:00
|
|
|
export function useApiHost() {
|
|
|
|
|
return baseUrl;
|
|
|
|
|
}
|