All Projects → michael-klein → hookuspocus

michael-klein / hookuspocus

Licence: other
hooks for all the functions!

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to hookuspocus

Fae
A functional module for Deno inspired from Ramda.
Stars: ✭ 44 (-26.67%)
Mutual labels:  functional, functional-js
react-use-hoverintent
React hook for hoverIntent
Stars: ✭ 16 (-73.33%)
Mutual labels:  hooks
react-functional-select
Micro-sized & micro-optimized select component for React.js
Stars: ✭ 165 (+175%)
Mutual labels:  functional
hooksy
Simple app state management based on react hooks
Stars: ✭ 58 (-3.33%)
Mutual labels:  hooks
dry-transformer
Data transformation toolkit
Stars: ✭ 59 (-1.67%)
Mutual labels:  functional
vector
A PHP functional programming library.
Stars: ✭ 19 (-68.33%)
Mutual labels:  functional
taroCloud
记日常、GitHub trending资讯小程序 taro-hooks + rematch+云开发
Stars: ✭ 25 (-58.33%)
Mutual labels:  hooks
use-app-state
🌏 useAppState() hook. that global version of setState() built on Context.
Stars: ✭ 65 (+8.33%)
Mutual labels:  hooks
react-fusionui
☢️ Nuclear power-up for your UI.
Stars: ✭ 13 (-78.33%)
Mutual labels:  hooks
resynced
An experimental hook that lets you have multiple components using multiple synced states using no context provider
Stars: ✭ 19 (-68.33%)
Mutual labels:  hooks
kyanite
A small purely functional library of curried functions, with great piping possibilities!
Stars: ✭ 26 (-56.67%)
Mutual labels:  functional-js
react-web-editor
The react-web-editor is a WYSIWYG editor library. you can resize and drag your component. It also has simple rich text editor
Stars: ✭ 191 (+218.33%)
Mutual labels:  hooks
Javascript-Interview-Preparation
A curated collection of javascript interview questions & solutions.
Stars: ✭ 163 (+171.67%)
Mutual labels:  hooks
hermes-js
Universal action dispatcher for JavaScript apps
Stars: ✭ 15 (-75%)
Mutual labels:  functional
arc-progress
🐳 Arc circular animation progress bar drawn by canvas, you can used in the react component, or no dependence.
Stars: ✭ 42 (-30%)
Mutual labels:  hooks
react-cool-form
😎 📋 React hooks for forms state and validation, less code more performant.
Stars: ✭ 246 (+310%)
Mutual labels:  hooks
linqjs
Perform queries on collections in the manner of C#s System.Linq in JavaScript
Stars: ✭ 14 (-76.67%)
Mutual labels:  functional
use-gesture
👇Bread n butter utility for component-tied mouse/touch gestures in React and Vanilla Javascript.
Stars: ✭ 6,624 (+10940%)
Mutual labels:  hooks
go-callbag
golang implementation of Callbag
Stars: ✭ 19 (-68.33%)
Mutual labels:  functional
async-chainable
An extension to Async adding better handling of mixed Series / Parallel tasks via object chaining
Stars: ✭ 25 (-58.33%)
Mutual labels:  hooks

hookus pokus logo

Add hooks to all the functions!



This is a small JavaScript library that will allow you to add hooks to any function. It clocks in at less than 1kb minified and gzipped.

It provides the basic hooks from which you can build more complex hooks:

  • useReducer
  • useState
  • useLayoutEffect
  • useEffect

Basic usage

If you don't know what they are yet, please learn about hooks from the react docs before you continue.

In order to enhance any function with hooks, you need to call it through pocus:

import { pocus, useState } from "hookuspocus";
function withHooks() {
  const [count, setCount] = useState(0);
  console.log(`function called ${count} times`);
  setCount(count + 1);
}
pocus(withHooks); // function called 0 times
pocus(withHooks); // function called 1 times
pocus(withHooks); // function called 2 times

It's important that the function you pass each pocus run is the same object every time (because we use it as key to store hook state across runs). If you can't guarantee this, you may pass a context object as second argument to connect subsequent runs:

import { pocus, useState } from "hookuspocus";
const context = { foo: "bar" };
function withHooks() {
  const [count, setCount] = useState(0);
  console.log(`function called ${count} times`);
  setCount(count + 1);
}
pocus(withHooks, context); // function called 0 times
pocus(withHooks, context); // function called 1 times
pocus(withHooks, context); // function called 2 times

You can also pass arguments to the wrapped function call by supplying them in an array as the first argument:

import { pocus } from "hookuspocus";
function withHooks(arg1, arg2) {
  console.log(`${arg1} ${arg2}!`); // Hello world!
}
pocus(["Hello", "world"], withHooks);

Internally, hookuspocus uses WeakMaps if possible to keep states between runs (and falls back to simple Maps). If your target browsers don't all have support for WeakMaps or you need to trigger cleanUp logic (e.g.: from useEffect) after a final run, you can call pocus with true as the last argument. This will call all remaining cleanUp functions and remove the function/context from the map:

import { pocus, useEffect } from "hookuspocus";
function withHooks() {
  useEffect(() => {
    //some effect here
    return () => {
      //something that needs cleaning up here
    };
  });
}
pocus(withHooks); //run it
pocus(withHooks, true); //clean up

hookuspocus also exports a helper function called fidibus that allows you to wrap a function (and context) and returns a new function you can just call repeatedly, for ease of use:

import { fidibus } from "hookuspocus";
const wrapped = fidibus((arg1, arg2) => {
  const [count, setCount] = useState(1);
  console.log(`${arg1} ${arg2} #${count}!`);
  setCount(count + 1);
});
wrapped("hello", "world"); // Hello world #1!
wrapped("hello", "world"); // Hello world #2!
wrapped("hello", "world"); // Hello world #3!

The wrapped function also has a cleanUp method attached to it:

wrapped.cleanUp(); // runs cleanUp logic

Creating hooks

There are two way to create hooks for use with hookuspocus:

  1. By composing new hooks from existing hooks. Hooks created in this manner from other frameworks like React should just work with hookuspocus (as long as they don't rely on built in hooks that are not provided by hookuspocus).

The useState hook is actually example of a hook created this way:

import { useReducer } from "./use_reducer";

export const useState = initialState => {
  const [state, dispatch] = useReducer(
    (_, action) => action.value,
    initialState
  );
  return [
    state,
    newState =>
      dispatch({
        value: newState
      })
  ];
};
  1. By creating new base hooks that use features provided by hookuspocus. This is accomplished with the hookus method. Let's look at the useEffect hook as an example:
import { hookus } from "./core";
export const useEffect = hookus((data, effect, values) => {
  if (
    !data.v ||
    (values &&
      !(
        values.length === data.v.length &&
        values.every(value => ~data.v.indexOf(value))
      ))
  ) {
    data.v = values;
    if (data.cleanUp) {
      data.cleanUp();
    }
    data.after = () => {
      data.cleanUp = effect();
    };
  }
});

hookus takes a function which represents the implementation of the hook and returns a hook function. Whenever this hook is called, the wrapped function will be called with a data object followed by whatever arguments where passed to the wrapper.

The data object is the means by which hooks can interact with the hookuspocus api and persist data between pocus calls. You can add any property to the data object for the latter purpose (in the above example data.v is used to store the values array passed as second argument to useEffect).

The data object also accepts 3 function properties: before, after and cleanup. Methods that are passed to these will be called before the next hook call, after the current pocus run or respectively when cleanUp is initiated.

useEffect uses data.after to execute effects after pocus runs and will manually call cleanUp before applying new effects if neccessary.

Intercepting hook calls

You might need to run some custom code when certain existing hooks are called. For instance, a view library might want to queue a re-render when setState from useState was called. For this purpose, hookuspocus provides the on method:

import { on, useReducer } from "./core";

on(useReducer, (data, reducer, initialArg, init) => {
  const [state, dispatch] = data.hook(data, reducer, initialArg, init);
  return [
    state,
    action => {
      const result = dispatch(action);
      if (state !== result) {
        //do something when the state has changed
      }
      return result;
    }
  ];
});

on basically allows you to wrap the existing hook with your own logic. You pass it the hook you want to intercept and a callback that will receive whatever arguments are passed to the hook at runtime.

Important: data.hook is the original hook function and you should always call this in case you need to perform the normal hook functionality in on. If you call useReducer, for example, in the above code, you will cause an infinite loop.

Incidentally, the above code is now by default provided by hookuspocus in the onStateChanged method:

import { onStateChanged } from "./core";

onStateChanged(() => {
  // do something
});

License

MIT License

Copyright (c) 2019 Michael Klein

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].