diff --git a/src/use-camera/use-camera.ts b/src/use-camera/use-camera.ts index 76ac158..b563ccf 100644 --- a/src/use-camera/use-camera.ts +++ b/src/use-camera/use-camera.ts @@ -1,5 +1,6 @@ -import { useEffect, useState } from "@rbxts/roact-hooked"; +import { useState } from "@rbxts/roact-hooked"; import { Workspace } from "@rbxts/services"; +import { useEventListener } from "../use-event-listener"; /** * Returns the current camera. Updates when the current camera changes. @@ -8,15 +9,11 @@ import { Workspace } from "@rbxts/services"; export function useCamera() { const [camera, setCamera] = useState(Workspace.CurrentCamera!); - useEffect(() => { - const connection = Workspace.GetPropertyChangedSignal("CurrentCamera").Connect(() => { - if (Workspace.CurrentCamera) { - setCamera(Workspace.CurrentCamera); - } - }); - - return () => connection.Disconnect(); - }, []); + useEventListener(Workspace.GetPropertyChangedSignal("CurrentCamera"), () => { + if (Workspace.CurrentCamera) { + setCamera(Workspace.CurrentCamera); + } + }); return camera; } diff --git a/src/use-event-listener/use-event-listener.ts b/src/use-event-listener/use-event-listener.ts index d934415..79e6346 100644 --- a/src/use-event-listener/use-event-listener.ts +++ b/src/use-event-listener/use-event-listener.ts @@ -21,7 +21,18 @@ type EventLike = type ConnectionLike = { Disconnect(): void } | { disconnect(): void } | (() => void); const connect = (event: EventLike, callback: Callback): ConnectionLike => { - if (typeIs(event, "RBXScriptSignal") || "Connect" in event) { + if (typeIs(event, "RBXScriptSignal")) { + // With deferred events, a "hard disconnect" is necessary to avoid causing + // state updates after a component unmounts. Use 'Connected' to check if + // the connection is still valid before invoking the callback. + // https://devforum.roblox.com/t/deferred-engine-events/2276564/99 + const connection = event.Connect((...args: unknown[]) => { + if (connection.Connected) { + return callback(...args); + } + }); + return connection; + } else if ("Connect" in event) { return event.Connect(callback); } else if ("connect" in event) { return event.connect(callback); diff --git a/src/use-latest-callback/README.md b/src/use-latest-callback/README.md index 5e7a51f..bc9e0c4 100644 --- a/src/use-latest-callback/README.md +++ b/src/use-latest-callback/README.md @@ -28,7 +28,7 @@ function Stepper({ onStep }: Props) { useEffect(() => { // Will always call the latest version of `onStep` - const connection = RunService.Heartbeat.Connect(onStepCallback); + const connection = RunService.RenderStepped.Connect(onStepCallback); return () => { connection.Disconnect(); diff --git a/src/use-latest/README.md b/src/use-latest/README.md index 124ca34..5b9c998 100644 --- a/src/use-latest/README.md +++ b/src/use-latest/README.md @@ -24,15 +24,9 @@ function Counter() { const [value, setValue] = useState(0); const latestValue = useLatest(value); - useEffect(() => { - const connection = RunService.Heartbeat.Connect(() => { - print(latestValue.current); - }); - - return () => { - connection.Disconnect(); - }; - }, []); + useEventListener(RunService.Heartbeat, () => { + print(latestValue.current); + }); return (