Skip to content

Commit 25b696c

Browse files
Merge pull request #117 from christopher-buss/feature/add-useful-hooks
feat(ui): add more useful hooks
2 parents 71a3f59 + 97447a5 commit 25b696c

File tree

6 files changed

+167
-12
lines changed

6 files changed

+167
-12
lines changed

eslint.config.ts

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import style, { GLOB_JS, GLOB_TSX } from "@isentinel/eslint-config";
1+
import style, { GLOB_TS } from "@isentinel/eslint-config";
22

33
export default style(
44
{
@@ -19,6 +19,18 @@ export default style(
1919
type: "natural",
2020
},
2121
],
22+
},
23+
typescript: {
24+
parserOptions: {
25+
project: "tsconfig.build.json",
26+
},
27+
tsconfigPath: "tsconfig.build.json",
28+
},
29+
},
30+
{
31+
files: [GLOB_TS],
32+
rules: {
33+
"no-param-reassign": "error",
2234
"ts/no-magic-numbers": [
2335
"error",
2436
{
@@ -29,20 +41,11 @@ export default style(
2941
},
3042
],
3143
},
32-
typescript: {
33-
parserOptions: {
34-
project: "tsconfig.build.json",
35-
},
36-
tsconfigPath: "tsconfig.build.json",
37-
},
38-
},
39-
{
40-
ignores: [GLOB_JS],
4144
},
4245
{
43-
files: [GLOB_TSX],
46+
files: ["src/client/ui/hooks/**/*"],
4447
rules: {
45-
"ts/no-magic-numbers": "off",
48+
"max-lines-per-function": "off",
4649
},
4750
},
4851
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { TableToString } from "@rbxts/rbx-debug";
2+
import { useEffect, useRef } from "@rbxts/react";
3+
4+
interface RenderInfo {
5+
name: string;
6+
renders: number;
7+
sinceLastRender: number;
8+
timestamp: number;
9+
}
10+
11+
/**
12+
* If you want to monitor and log how a component renders, you can use the
13+
* `useRenderInfo` hook. This hook records the render count, the time difference
14+
* between renders, and the current render timestamp. This hook is especially
15+
* useful for development purposes, as it helps developers understand how a
16+
* component behaves in terms of rendering, and allows them to improve
17+
* performance and detect potential problems.
18+
*
19+
* @param name - The name of the component you are monitoring.
20+
* @param logFunction - The function to use for logging. Defaults to `print`.
21+
* @param logEnabled - Whether or not logging is enabled.
22+
* @returns The render information.
23+
* @see https://github.com/cool-organization/rbx-hooks/blob/main/src/debugging/use-render-info.ts
24+
*/
25+
export function useRenderInfo(
26+
name = "Unknown",
27+
logFunction = print,
28+
logEnabled = true,
29+
): Readonly<RenderInfo> {
30+
const count = useRef(0);
31+
const lastRender = useRef<number>();
32+
const currentTime = os.clock();
33+
34+
count.current += 1;
35+
36+
useEffect(() => {
37+
lastRender.current = os.clock();
38+
});
39+
40+
const sinceLastRender = lastRender.current !== undefined ? currentTime - lastRender.current : 0;
41+
const info = {
42+
name,
43+
renders: count.current,
44+
sinceLastRender,
45+
timestamp: currentTime,
46+
};
47+
48+
if (logEnabled) {
49+
logFunction(TableToString(info, true));
50+
}
51+
52+
return table.freeze(info);
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useEffect, useRef } from "@rbxts/react";
2+
3+
/**
4+
* A simple function that checks how many times the component has been rendered.
5+
*
6+
* @returns The render count.
7+
* @see https://github.com/cool-organization/rbx-hooks/blob/main/src/debugging/use-renders-spy.ts
8+
*/
9+
export function useRendersSpy(): number {
10+
const count = useRef(0);
11+
useEffect(() => {
12+
count.current += 1;
13+
});
14+
15+
return count.current;
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import Object from "@rbxts/object-utils";
2+
import { TableToString } from "@rbxts/rbx-debug";
3+
import { useEffect, useRef } from "@rbxts/react";
4+
import { t } from "@rbxts/t";
5+
6+
interface ChangeObject {
7+
from: unknown;
8+
to: unknown;
9+
}
10+
11+
/**
12+
* A simple hook that checks which prop caused the component to re-render.
13+
*
14+
* @param name - The name of the component.
15+
* @param props - The props of the component.
16+
* @param logFunction - The function to use to log the changes.
17+
* @param logEnabled - Whether or not to log the changes.
18+
* @see https://github.com/cool-organization/rbx-hooks/blob/main/src/debugging/use-why-did-you-update.ts
19+
*/
20+
export function useWhyDidYouUpdate(
21+
name: string,
22+
props: Record<string, unknown>,
23+
logFunction = print,
24+
logEnabled = true,
25+
): void {
26+
const previousProps = useRef<Record<string, unknown>>({});
27+
28+
useEffect(() => {
29+
const previous = previousProps.current;
30+
31+
const allKeys = Object.keys({ ...previous, ...props });
32+
const changesObject: Record<string, ChangeObject> = {};
33+
34+
for (const key of allKeys) {
35+
let previousValue = previous[key];
36+
let updatedValue = props[key];
37+
38+
if (previousValue !== updatedValue) {
39+
// We need to remove the meta tables from the previous and new
40+
// values to ensure we don't call any meta-methods on the props.
41+
if (t.table(previousValue)) {
42+
// eslint-disable-next-line ts/no-non-null-assertion -- Required to remove meta table
43+
previousValue = setmetatable(table.clone(previousValue), undefined!);
44+
}
45+
46+
if (t.table(updatedValue)) {
47+
// eslint-disable-next-line ts/no-non-null-assertion -- Required to remove meta table
48+
updatedValue = setmetatable(table.clone(updatedValue), undefined!);
49+
}
50+
51+
changesObject[key] = {
52+
from: previousValue,
53+
to: updatedValue,
54+
};
55+
}
56+
}
57+
58+
if (logEnabled && next(changesObject)[0]) {
59+
logFunction(name + " " + TableToString(changesObject, true));
60+
}
61+
62+
previousProps.current = props;
63+
});
64+
}

src/client/ui/hooks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from "./use-defined";
22
export * from "./use-motion";
3+
export * from "./use-orientation";
34
export * from "./use-premium";
45
export * from "./use-rem";
56
export * from "./use-selector";
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useViewport } from "@rbxts/pretty-react-hooks";
2+
import { useState } from "@rbxts/react";
3+
4+
type Orientation = "landscape" | "portrait";
5+
6+
export function useOrientation(): Orientation {
7+
const viewportBinding = useViewport(viewport => {
8+
setOrientation(viewport.Y > viewport.X ? "portrait" : "landscape");
9+
});
10+
11+
const [orientation, setOrientation] = useState<Orientation>(
12+
viewportBinding
13+
.map(viewport => (viewport.Y > viewport.X ? "portrait" : "landscape"))
14+
.getValue(),
15+
);
16+
17+
return orientation;
18+
}

0 commit comments

Comments
 (0)