Skip to content

Instantly share code, notes, and snippets.

@cdaringe
Last active August 15, 2018 00:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cdaringe/d2ce14d8b078b9d8860374591a86325f to your computer and use it in GitHub Desktop.
Save cdaringe/d2ce14d8b078b9d8860374591a86325f to your computer and use it in GitHub Desktop.
react-super-hoc-design

problem

higher-order components must provide

  • easily extensible layouts
  • good default components
  • mechanisms to control default components
  • easily extensible/swappable components

providing a "free" high-level tree of components whilst offering low-level extensibility can be seen as conflicting objectives.

  • providing default components intrinsically defines and locates nodes on the DOM
  • needing extensinsibility is specifically the undoing of providing default trees/DOM

providing an API that meets both goals is TBD

research

other component libraries have various patterns that help composition.

  • SUIR has a practice of exporting supporting Components off a root component. e.g. <Form.Field />
    • this is a great short hand that internally does function/component composition

but what about when we want attribute composition without extra DOM?

motivating example

let's take the case of making a <LoginPage /> screen. i'm working on the <AuthPane /> component, which must support a variety of auth states:

  • sign in
  • create account
  • forgot password
  • reset password
  • sign out
  • ...maybe others

the <AuthPane /> should host all of the above contents depending on what state it's in.

solution 1

create new components for each state.

const SignInPane =
  <AuthPane>
    <Input un />
    <Input password />
  </AuthPane>
const SignOutPane =
  <AuthPane>
    <Button onClick={signOut} />
  </AuthPane>

drawbacks

  • we lose extensiblity of layout
    • components are firmly positioned
    • we've prescribed an exact component for each control, and each control is not extensible
  • we duplicate <AuthPane /> all over the place, rather than use a common reference in mem

solution 2

create HOC's that allow overrides, but provide sensible defaults. consider just the <AuthPane /> with signout support.

<AuthPane signOut />
// by default renders an empty <SignOut /> as a child of the `<AuthPane />`
<AuthPane signOut={<SignOut onSignOut={onSignout}} /> // enables fine customization of the logout
// but ^ is kind of annoying, because now you must import <SignOut /> to customize it
<AuthPane signOut={{ onSignout }} /> // so support POJOs that will get passed as props
// <SignOut {...{ onSignout }} /> so now the user gets _really easy customization_
<AuthPane signOut={<p>or ditch the default component altogether</p>} />
// for ultimate flexibility
<AuthPane>
  <span>some content</span>
  <AuthPane.SignOut className='auth-pane__signout--my-app-override' />
  <span>other content</span>
</AuthPane>
// but this is risky, because in this HOC, <AuthPane.Logout /> value is both
// locating the React.Element into the DOM _and_ providing a component set.
// props.signOut and <AuthPane.SignOut /> cannot be used in tandem
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment