npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-unhook

v0.4.1

Published

React hooks without hooks - a collection of hook-like Null Components

Downloads

6

Readme

react-unhook

React hooks without hooks - a collection of hook-like Null Components

npm Build Status Coverage Status TypeScript Support

About

react-unhook attempts to emulate some of the functionality and segmentation aspect of react hooks, packaging it into a standalone "Null Components" (components that render null) without the use of React hooks under-the-hood.

(Note: This is not about avoiding hooks. Just an alternative to some of it).

Motivation

React Hooks are a new addition to React 16.8 and it changes the way we have been approaching React components, formalising new ways of encapsulating logic in our components.

Taking inspiration of that, we can make use of the existing lifecycle methods to achive some behaviours of React hooks via Null Components. This allows us to achieve similar code style and logic encapsulation of React hooks, aside from low-level / library optimization of hooks.

Limitations

With that said, there are some limitations to this Null Component pattern. Stateful hooks (eg: "useReducer") cannot be emulated easily as we are not able to expose functions of the component without resorting to anti-patterns (eg: using React.createRef to access component methods).

However, this Null Component pattern works well for "listeners", "workers" or "value-change triggers" (triggering of a function after a change in value). For example, listening to geolocation changes, interval calls, data fetching on parameter changes etc.

Use Case

// Imagine that you have a signup form that on certain value change,
// we want to fetch things or asynchronously set values
// Using "Null Components" we can declaratively define those effects.

function SignupForm(props) {
  return (
    <Fragment>
      <Input name="input-one" />
      <Input name="input-two" />
      <Input name="input-three" />

      <FetchWhenInputOneIsFilled name="action-one" />
      <ValidateWhenTwoIsDirty name="action-two" />
      <UpdateInputThreeWhenTwoIsValid name="action-three" />
    </Fragment>
  );
}

Usage

npm install react-unhook --save
import { UseCallback, UseEffect } from 'react-unhook';

Example

These examples are adopted from React's official docs on hooks. i.e. https://reactjs.org/docs/hooks-effect.html

The unhook examples makes use of withState HOC (1, 2) to keep the code style closer to the hooks examples. You can also manage your state using a normal class.

Demo / Storybook

Examples are also available at http://yeojz.github.io/react-unhook

Effects Without Cleanup

Using Hooks:

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

Using Unhook:

function Example(props) {
  // assumes you're using withState HOC.
  // eg: withState('count', 'setCount', 0)(Example);
  const { count, setCount } = props;

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>

      <UseEffect
        fn={() => {
          document.title = `You clicked ${count} times`;
        }}
      />
    </div>
  );
}

Effects With Cleanup

Using Hooks:

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // Specify how to clean up after this effect:
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

Using Unhook:

function FriendStatus(props) {
  // withState('isOnline', 'setIsOnline', null)(FriendStatus);
  const { isOnline, setIsOnline } = props;

  return (
    <Fragment>
      {isOnline === null ? 'Loading' : isOnline ? 'Online' : 'Offline'}

      <UseEffect
        fn={() => {
          function handleStatusChange(status) {
            setIsOnline(status.isOnline);
          }

          ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
          // Specify how to clean up after this effect:
          return function cleanup() {
            ChatAPI.unsubscribeFromFriendStatus(
              props.friend.id,
              handleStatusChange
            );
          };
        }}
      />
    </Fragment>
  );
}

Optimizing Performance

Using Hook:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

Using Unhook:

<UseEffect
  fn={() => {
    document.title = `You clicked ${count} times`;
  }}
  inputs={[count]}
/>

API Reference

Note: The comparator function, by default, follows React Hook's areHookInputsEqual method, which uses Object.is to compare the values in the array.

All unhook components make use of UseCallback and UseEffect at their core.

Many of the components are inspired by hooks from react-use, but re-implmented using react-unhook's <UseEffect /> instead of actual React Hooks.

Core

UseEffect

Component which emulates useEffect hook.

interface Props {
  fn: () => void | Noop;
  inputs?: Array<any>;
  comparator?: EqualityFn;
}

UseCallback

The difference between UseEffect and UseCallback is that the function passed into UseEffect may return a "clean-up" function which will be executed when unmounting the component. In most cases, you can just utilise UseEffect.

interface Props {
  fn: () => void;
  inputs?: Array<any>;
  comparator?: EqualityFn;
}

Lifecycle

UseEffectOnUpdate

Only runs the callback when inputs change and not during mounting.

interface Props {
  fn: () => void | VoidFn;
  inputs: any[]; // unlike UseEffect, this is required.
  comparator?: EqualityFn;
}

UseEffectOnce

Alias method using UseEffect with prop.inputs preset to []

interface Props {
  fn: () => void;
}

UseMount

Calls a function when the component is mounted

interface Props {
  fn: () => void;
}

UseUnmount

Calls a function when the component will unmount.

interface Props {
  fn: () => void;
}

Timing

UseInterval

Calls the function at every specified interval (in milliseconds), eg: Polling.

interface Props {
  fn: () => void;
  time: number;
}

UseTimeout

Calls the function after the specified wait time (in milliseconds)

interface Props {
  fn: () => void;
  time: number;
}

Sensors

UseGeolocation

Tracks user's geographic location.

interface Props {
  fn: (
    error: GeolocationPositionError | null,
    data: GeolocationPosition | null
  ) => void;
  watch?: boolean;
  options?: PositionOptions;
}

UI

UseMouseOut

Fires a callback when mouse leaves target element.

interface Props {
  fn: () => void;
  target: () => HTMLElement | Document | Window;
  capture?: boolean;
}

License

react-unhook is MIT licensed