Skip to content

Instantly share code, notes, and snippets.

@aleclarson
Last active November 2, 2025 22:43
Show Gist options
  • Save aleclarson/8aac3cca138751bd4da89b8ccb1ec2f6 to your computer and use it in GitHub Desktop.
Save aleclarson/8aac3cca138751bd4da89b8ccb1ec2f6 to your computer and use it in GitHub Desktop.
Remix 3 "track" utility
// 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)
}
}
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