feat(react-utils): preliminary component hooks - #812
Open
Dashice wants to merge 2 commits into
Open
Conversation
🦋 Changeset detectedLatest commit: 548e073 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## feature/use-ref-object #812 +/- ##
==========================================================
+ Coverage 71.76% 73.70% +1.94%
==========================================================
Files 68 75 +7
Lines 1066 1160 +94
Branches 268 289 +21
==========================================================
+ Hits 765 855 +90
Misses 237 237
- Partials 64 68 +4
🚀 New features to boost your workflow:
|
Dashice
force-pushed
the
feat/react-utils-preliminary-component-hooks
branch
from
August 4, 2026 13:03
621a5ab to
e0f8bd4
Compare
Contributor
|
Such a nice read, let's aim to get this in 🌟 |
Dashice
force-pushed
the
feat/react-utils-preliminary-component-hooks
branch
from
August 5, 2026 13:10
e0f8bd4 to
a48a1ac
Compare
Dashice
force-pushed
the
feat/react-utils-preliminary-component-hooks
branch
from
August 6, 2026 13:49
a48a1ac to
6fd64ec
Compare
Dashice
marked this pull request as ready for review
August 6, 2026 13:50
Dashice
force-pushed
the
feat/react-utils-preliminary-component-hooks
branch
from
August 31, 2026 11:43
6fd64ec to
548e073
Compare
Dashice
force-pushed
the
feature/use-ref-object
branch
2 times, most recently
from
August 31, 2026 12:46
f6f3aec to
2a23b84
Compare
adamsoderstrom
approved these changes
Sep 3, 2026
adamsoderstrom
left a comment
Member
There was a problem hiding this comment.
Looks great, @Dashice!
Awesome read! 🌟
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Philosophy
This PR showcases the philosophy behind cooks (component-hooks) and includes native React hook to cook conversions and one custom hook conversion. This is done to showcase the various methodologies which can appear when creating future cooks.
Anatomy
UseStateexposes a state and setter via render function.UseIntersectionObserverexecutes callback when<div />intersects the viewport and updates state ofUseState.UseStickydoesn't require render function, and attachesdata-stuckto<div />from within.useRefis used in this context.UseStickycook does not requireref, even though hookuseStickydoes as it is created internally.UseStickylogic is disabled when<div />is not intersecting the viewport.<div />simultaneously.Purpose
Component hooks allow hook-logic to be conditional, initialized in JSX-loops, and re-render only a portion of JSX instead of entire component. This avoids the necessity to create abstract components that encapsulate frequent state updates. They can also allow developers to circumvent needing
"use client";in certain contexts.Ruleset
Cooks require specific wiring to be viable; and developers should adhere to a standard in order to deploy cooks at-scale. They are not created equal, and there are three general branching approaches. Some universal rules exist between cooks:
false,nullorundefined.false,nullorundefined.refreftoRefObjectwithuseRefObject(ref).refis provided, or cook requires arefattached on an element, and child isFragment; throw error.refis provided, or cook requires arefattached on an element; forward it usingcloneElement.refis not provided, and cook doesn't require arefattached on an element; do not forward it.cloneElementFragment. In such scenario, render child without forwarding props.false,null,undefined. In such scenario, render child without forwarding props.{ ref, ...cookProps, ...cookComputedProps ...childProps }, ensures that attributes explicitly set further down DOM tree overwrite attributes forwarded down from above.refset before cook can forward its ownref, an error should be thrown, prompting to moverefonto the cookchildrenorcloneElement(children, ...)Fragment.Design Approach
When creating a cook, a developer must choose between three general render approaches; Element, Render Function, Hybrid. When to use which is outlined below.
Element
If the underlying hook does not return anything meaningful that is worth exposing as a variable; set
childrento typeReactElement. Useful for hooks that do not return anything, adding event listeners or operating on arefpassively.Render Function
If the underlying hook returns something meaningful, and cannot be represented in the DOM as an attribute; set
childrento(prop: X) => ReactElement. Useful for hooks that return complex shapes, multiple values, callbacks, etc.Hybrid
If the underlying hook returns something meaningful, but can be simplified, or most commonly only a
booleanorstringunion is consumed for styling purposes; injecting adata-*attribute from within the cook is possible like so:cloneElement(childElement, {...props, 'data-attr': value });. This allows consumers to either pass a render function or an element as a child, in other words; a hybrid approach.In this PR, all three methods are used: UseEffect (Element), UseState (Render function), UseRTL (Hybrid).
Guiding Consumer Adherence to Ruleset
Have a look at the following code, that on-paper looks reasonable:
Adhering to the ruleset above; a TS error will indicate that
UseCookmay only have one child, guiding the user to adjust their code to:Next, if the user saves their file, the following error would be thrown in a browser environment:
If this was not enforced, either the
refcreated inside the cook, or the ref provided by the user would have no effect. By guiding the user to moveref={ref}onto the<UseCook ref={ref} />, the passedrefwill be used as the base; instead of therefcreated inside the cooks internal code. In situations where multiple cooks act on a singular element, this will ensure that the consumer will be able to perform DOM manipulations on their desired element, whilst simultaneously allowing all cooks to perform their own actions on the same element.This leads to the correct code:
Fragments
As cooks work with
ReactElement(a super ofFragment),Fragment's are allowed on cooks that do not require arefto be attached on its child.Ensure that any cook that requires a
refto be attached on an element throws an error in the cook chain.This should error, despite
Fragmentbeing aReactElement.UseDragScrollmust attach itself on an element in order to function.Fragment's are singular elements, but they are transient and what remains in DOM are three<div />elements, whichUseDragScrollwill not be able to determine which to attach itself to.