Skip to content

Type safe and Zero-cost React library to work with higher-order components (HOCs)

License

Notifications You must be signed in to change notification settings

leobastiani/react-with-hoc

Repository files navigation

react-with-hoc

Type safe and Zero-cost React library to work with higher-order component (HOC)

Documentation NPM version Build Typescript Bundle size Bundle dependency License

Getting Started

Read the docs

Install with npm

npm install react-with-hoc

Or install with yarn

yarn add react-with-hoc

Usage example

Hello World

import { withDefault, withOverride, withHocs } from "react-with-hoc";

export const Hello = (() => {
  function Hello({ name }: { name: string }) {
    return <div>Hello {name}!</div>;
  }

  return withHocs([withDefault({ name: "World" })])(Hello);
})();

// <Hello /> is equivalent to <div>Hello World!</div>
// <Hello name="You" /> is equivalent to <div>Hello You!</div>

export const HelloYou = withOverride("name", "You")(Hello);

// <HelloYou /> is equivalent to <div>Hello You!</div>
// <HelloYou name="..." /> is a typescript error ❌

Example with react-query

Lets suppose you have the following code and then you need a query inside your App component

const queryClient = new QueryClient();

function App() {
  // Oops... ❌
  // This is an error because App is not wrapped by QueryClientProvider
  const query = useQuery({...});

  return (
    <QueryClientProvider client={queryClient}>
      <>...</>
    </QueryClientProvider>
  );
}

export default App;

Using react-with-hoc, you can easily fix this with:

import {withWrapper, withOverride} from "react-with-hoc";

const queryClient = new QueryClient();

function App() {
  // ✅
  const query = useQuery({...});

  return (
    <>...</>
  );
}

export default withWrapper(
  withOverride({ client: queryClient })(QueryClientProvider)
)(App);

// for didactic purpose, the following code could also be applied
// const MyQueryClientProvider = withOverride({ client: queryClient })(QueryClientProvider)
// export default withWrapper(MyQueryClientProvider)(App);

Using IIFE

import { withWrapper, withOverride, withHocs } from "react-with-hoc";

const queryClient = new QueryClient();

const App = (() => {
  function App() {
    // ✅
    const query = useQuery({...});

    return <>...</>;
  }

  return withHocs([
    withWrapper(withOverride({ client: queryClient })(QueryClientProvider)),
  ])(App);
})();

export default App;

Clock

Check out an entire project with react-with-hoc, see the demo and try to imagine creating a reusable component with the same flexibility

Take a look on how simple it's the final result

const RedHour = withOverride("color", "red")(HourPointer);

const Square = withStyle({
  borderRadius: 0,
})(ClockCircle);

function App(): JSX.Element {
  return (
    <>
      <div>
        <h1>The default clock</h1>
        <Clock />
      </div>
      <div>
        <h1>#1 Variant: without minute marks</h1>
        <Clock MinuteMarks={null} />
      </div>
      <div>
        <h1>#2 With a red hour pointer</h1>
        <Clock HourPointer={(): typeof RedHour => RedHour} />
      </div>
      <div>
        <h1>#3 Inside a square</h1>
        <Clock Circle={(): typeof Square => Square} />
      </div>
    </>
  );
}