hookly

A React plugin to present hooks as higher order functions

Usage no npm install needed!

<script type="module">
  import hookly from 'https://cdn.skypack.dev/hookly';
</script>

README

hookly

This project aims to overcome some of the issues in React Hooks. It should also allow for easier migration from Recompose to React Hooks.

Consider the following example:

const Counter: FunctionComponent<{ name: string }> = ({ name }) => {
    const [count, setCount] = useState(1);
    return <div>
        <p>Hi {name}, You clicked {count} times</p>
        <button onClick={() => setCount(count + 1)}>
            Click me
      </button>
    </div>;
}

It is a functional component with state. It mixes both the state, and the presentation in the same code.

We can fix this by splitting the presentation and the state into two components, the presentation component, and a container component.

const Counter:
    FunctionComponent<{ count: number, name: string, setCount: Dispatch<SetStateAction<number>> }> =
    ({ name, count, setCount }) => {
        return <div>
            <p>Hi {name}, You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>;
    }

const CounterContainer: FunctionComponent<{ name: string }> = ({ name }) => {
    const [count, setCount] = useState(1);
    return <Counter name={name} count={count} setCount={setCount} />
}

More abstracted again

const Counter:
    FunctionComponent<{ count: number, name: string, setCount: Dispatch<SetStateAction<number>> }> =
    ({ name, count, setCount }) => {
        return <div>
            <p>Hi {name}, You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>;
    }

const CounterContainer: FunctionComponent<{ name: string }> = props => {
    const [count, setCount] = useState(1);
    const finalProps = { ...props, count, setCount };
    return <Counter {...finalProps} />;
}

It is possible to abstract the container component further, eventually creating a generic higher order component that will allow you to apply useState hooks to any component, allowing you to map the hook results to the properties of the component.

interface IProps { count: number; name: string; setCount: setState<number>; }

const Counter: FunctionComponent<Props> = ({ count, setCount, name }) =>
  <div>
    <p>Hi {name}, You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>
      Click me
    </button>
  </div>;
// ContainerCounter has type React.FunctionComponent<{ name: string }> but TS will infer this
const CounterContainer = stateWrapper(1, ([count, setCount]) => ({ count, setCount }))(Counter);