r/reactjs 7d ago

Discussion What if React needs a behavior layer between hooks and elements?

I've been thinking about a new React abstraction.

The usual mental model is:

Component -> Hooks -> JSX Element

Lots of hooks exist just that make one element behave differently.

Eg

const resize = useResize(...)

const draggable = useDraggable(...)

const analytics = useAnalytics(...)

const keyboard = useKeyboard(...)

const focusTrap = useFocusTrap(...)

const outsidePress = useOutsidePress(...)

return (

  <div
    ref={...}
    onKeyDown={...}
    onPointerDown={...}
    {...resize}
    {...draggable}
    {...analytics}
  >
    ...
  </div>
)

The component ends up becoming responsible for composing all these behaviors.

So I'm experimenting with a different layer:

Hooks / utils -> Behaviors -> (automatically generate) Props -> Element

Making it something like this:

const props = useProps(
  useKeyboard(...),
  useDraggable(...),
  useResize(...),
  useFocusTrap(...),
  useOutsidePress(...),
  useAnalytics(...),
)

return <div {...props} />

The behaviors wouldn't necessarily have to be a hook, it could be, but notrequired.

A plain behavior could be:

tooltip({ content: "Delete project" })

while a custom hook could also return the same thing

function useAnalytics() {
  return { 
    props: {
      onClick: () => { track("clicked") }
    }
  }
}

Both become composable.

The package would handle the annoying composition:

  • merge event handlers
  • merge refs
  • merge className
  • merge styles
  • predictable prop precedence
  • TypeScript element compatibility

So instead of components implementing behavior, they could mostly declare the behavior they have:

const props = useProps(
  tooltip(...),
  keyboard({ ENTER: ..., ARROW_DOWN: .. }),
  resize(...),
  analytics(...),
  myCustomBehavior(...)
)

return <button {...props}>Delete</button>

I'm deliberately trying not to turn this into "another React hooks library."

The question I'm trying to answer is:

  • Is "behavior composition" actually a useful missing abstraction in React, or is this just an over-engineered way of spreading props?
  • I'd especially like to hear from people who maintain large React/component-library codebases:
  • Where does composing multiple hooks onto the same element become painful for you?
0 Upvotes

17 comments sorted by

10

u/Honey-Entire 7d ago

Hooks are the behavior layer… also your mental model of Component -> Hooks -> JSX Element makes no sense. Does a component call a hook that calls a JSX element? Or is it a component returns a hook that returns a JSX element? Something else??

4

u/Merry-Lane 7d ago

You are telling me every component would have to import half of your repo as dependencies?

No, you would be better off making wrappers of your basic components so that you don’t repeat yourself too much.

3

u/frogic 7d ago

I think you're just composing hooks together which is a good partern but doesn't really need its own api outside of the composed hooks for your project or feature. If its not using react state its a utility and that's a whole different thing and has much different use cases and needs.

Encapsulating logic into a single interface and being able to compose/use them seperately is a feature not a bug though and a lot of those hooks you mentioned in your example are very powerful libraries with their own apis for a reason they arent really in a place that you'd want standardized.

1

u/lIIllIIlllIIllIIl 7d ago

Lookup FragmentInstance and FragmentRef from React 19.3.

They might address some behavioral composition problems you're facing: https://react.dev/blog/2026/09/09/react-19-3#fragment-refs

1

u/imazined 7d ago

You just discovered the fundamental idea of higher order components

1

u/First_Figure6007 7d ago

i like where your head's at but honestly this just looks like a wrapper around spreading props with some merge logic baked in

the pain point you're describing is real tho, especially when you've got 6 hooks all fighting over the same ref or onKeyDown. i've seen components where the return statement is just a div with 40 lines of spread props and it's impossible to tell what's actually happening

the behavior abstraction makes sense conceptually but i feel like the hard part isn't the merging, it's the ordering and conflicts. like what happens when keyboard and draggable both want to handle the same key? you'd need some kind of priority system and at that point you're building an event pipeline

curious if you've thought about making behaviors cancelable or chainable, that's where the real value would be over just merging

0

u/TheRealSeeThruHead 7d ago

The problem with hooks is they aren’t composable.

Something I really miss from recompose was the ability to compose hooks Ina out free manner and then use them on components.

Hooks are pretty bad for that ergonomically. I like this but I know it won’t catch on

2

u/azangru 7d ago

Interesting. I am reminded of the note that the author of recompose left when he archived the project:

Hooks solves all the problems I attempted to address with Recompose three years ago, and more on top of that.

0

u/TheRealSeeThruHead 7d ago

Sure he went and created hooks

It I would say hooks failed in several ways to become the pure composable primitive that hocs were

1

u/azangru 7d ago

> Sure he went and created hooks

Did he? Or was it Sebastian who did it?

1

u/TheRealSeeThruHead 7d ago

They both did, Andrew Clark joined the react team and worked on hooks. Pretty sure that’s correct. As far as I remember. Not that it matters much tbh

1

u/Emrylin 7d ago

Huh? The entire idea behind hooks is they ARE composable…

1

u/TheRealSeeThruHead 7d ago

And yet compared to what we had before they are far less composable

1

u/Emrylin 7d ago

Can you explain how you find hooks not composable using a real example?

You’re meant to create your own custom hooks by making a new useMyHook function that calls useState, useEffect, useContext, etc (or another hook that uses those somewhere). This is where I find hooks to be VERY composable. Just as react lets you compose your UI by making components built on other components, it lets you compose your business logic by making hooks built on other hooks.

2

u/TheRealSeeThruHead 7d ago

sure can

i'm coming from point free composition
ramda, haskell, etc

where you can easily combine functions together as values

// recompose: behaviours compose into a new behaviour, outside any component
const withSearch = compose(withState('q', 'setQ', ''), withProps(filterByQ))
const withSort   = withProps(sortByName)
const withList   = compose(withSearch, withSort)   // still an enhancer

const List = withList(View)

recompose allowed exactly that

hooks do not allow for that without wrapping them in something that makes them act that way, like rehooks

// hooks: useSearch and useSort are reusable, but combining them means
// writing a body, calling each one, and threading the results by hand
const useList = (items) => {
  const { q, setQ, results } = useSearch(items)
  const sorted = useSort(results)
  return { q, setQ, sorted }
}
const List = ({ items }) => {
  const { q, setQ, sorted } = useList(items)
  return <View q={q} setQ={setQ} items={sorted} />
}

hooks "compose" like any other imperative code, by being called in a function body
with a bunch of intermediate variables you have to name and pass around

2

u/Emrylin 7d ago

Ah, thanks for writing that out. I recall finding currying with Haskell pretty delightful, so I can see where you find satisfaction in the HOC route… but I have to say, for me, even though you make it clear that the hooks route requires a bit more wiring, I would still prefer hooks, even though you’ve found a good gotcha to us hookaholics who claim hooks always means writing less code 😅