Last active
November 2, 2025 22:43
-
-
Save aleclarson/8aac3cca138751bd4da89b8ccb1ec2f6 to your computer and use it in GitHub Desktop.
Remix 3 "track" utility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Track props access and selectively rerun callbacks on rerender. | |
| export function track<Props extends object>( | |
| ...callbacks: ((props: Props) => void)[] | |
| ) { | |
| const tracked = callbacks.map((callback) => new TrackedCallback(callback)) | |
| let oldProps: Props | undefined | |
| return (props: Props) => { | |
| tracked.forEach((callback) => { | |
| if (!oldProps || callback.shouldUpdate(props, oldProps)) { | |
| callback.update(props) | |
| } | |
| }) | |
| oldProps = props | |
| } | |
| } | |
| class TrackedCallback<Props extends object> { | |
| accessedProps = new Set<keyof Props>() | |
| constructor(readonly callback: (props: Props) => void) {} | |
| shouldUpdate(props: Props, oldProps: Props) { | |
| for (const prop of this.accessedProps) { | |
| if (props[prop] !== oldProps[prop]) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| update(props: Props) { | |
| this.accessedProps.clear() | |
| props = new Proxy<any>(props, { | |
| get: (props, prop) => { | |
| this.accessedProps.add(prop as keyof Props) | |
| return props[prop] | |
| }, | |
| }) | |
| this.callback(props) | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| declare class Foo { | |
| constructor(foo: string) | |
| foo: string | |
| } | |
| function Comp(props: { foo: string }) { | |
| let foo: Foo | |
| type Props = typeof props | |
| const update = track((props: Props) => { | |
| // Recreate foo whenever `props.foo` changes. Other props are ignored. | |
| foo = new Foo(props.foo) | |
| }) | |
| return (props: Props) => { | |
| update(props) // Selectively rerun callbacks on rerender. Minimal boilerplate. | |
| return <div>{foo.foo}</div> | |
| } | |
| } | |
| function Comp2(props: { foo: string }) { | |
| let foo = new Foo(props.foo) | |
| type Props = typeof props | |
| const update = track((props: Props) => { | |
| // Update foo whenever `props.foo` changes. Other props are ignored. | |
| foo.update(props.foo) | |
| }) | |
| return (props: Props) => { | |
| update(props) // Selectively rerun callbacks on rerender. Minimal boilerplate. | |
| return <div>{foo.foo}</div> | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment