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

ceci-hook

v1.0.1

Published

Hook-style behavior reuse -- in any framework!

Downloads

1

Readme

ceci-hook

Hook-style behavior reuse -- in any framework!

Why do I want this?

I designed the API while I was working on some old React 15 code. I wanted the reusability and encapsulation that hooks provided, but couldn't use them for compatibility reasons. Once I realized that I only needed to permit a very specific use case, the API design became clear.

This package does not make any assumptions about your application and will work equally well in any frontend framework. (It's possible you could use it on the backend too, but I can't think of a use case.)

Installation

npm install --save ceci-hook

Usage

This package exposes a single export, createEffect, which accepts a side-effect callback describing any necessary setup and cleanup. createEffect returns an "almost-hook", which you must call manually to update. The almost-hook passes all of its arguments to your side-effect, and only calls the side-effect when its arguments change.

Avoiding trouble

  • You must create a new almost-hook for each component. Otherwise, you'll end up with strange, broken behavior. An easy way to create a new almost-hook is to set it up in a constructor.
  • You have to call the almost-hook yourself when it updates. There's absolutely no magic going on here -- your code won't run if you don't call the almost-hook at the appropriate time.
  • Use closures if you don't want certain arguments considered for equality. You have two places where you can provide information to your side-effect callback: during the callback's creation, and when you call the resulting almost-hook. Arguments passed to the almost-hook will be checked to see whether they've changed. Only use these arguments for values which will require you to update your side-effect behavior; pass other values in via closure. (See "Using the Effect Hook" in the React documentation for an example of passing document.title in this way.)
  • Make sure you always provide the arguments in the same order. If you must call your almost-hook from multiple places, double-check that you haven't swapped the order or forgotten any arguments. This could lead to difficult bugs.

Example (React)

import React from 'react';
import PropTypes from 'prop-types';
import createEffect from 'ceci-hook';

const noop = () => {};

export default class EffectExample extends React.Component {
  // Create the almost-hook.
  _fetchGreeting = createEffect((name) => {
    // `name` is set to `null` on component unmount, so don't do any setup and
    // don't return a cleanup callback either.
    if (!name) return null;

    let setGreeting = this._setGreeting;
    fetch(`/api/greet?username=${name}`)
      .then((response) => response.text())
      .then((greeting) => setGreeting(greeting));

    return () => {
      // Low-effort way to prevent a race condition.
      setGreeting = noop;
    }
  });

  // Utility function.
  _setGreeting = (greeting) => this.setState({ greeting });

  componentDidMount() {
    const {name} = this.props;
    
    // Call the almost-hook on mount.
    this._fetchGreeting(name);
  }

  componentDidUpdate() {
    const {name} = this.props;
    
    // Call the almost-hook on update.
    // Notice that because `ceci-hook` already checks whether your dependencies
    // have changed, you don't need to include any conditional logic here.
    this._fetchGreeting(name);
  }

  componentWillUnmount() {
    // Call the almost-hook on unmount.
    this._fetchGreeting(null);
  }

  render() {
    return (<div>Your customized greeting: {this.state.greeting}</div>);
  }
}

Example (Vue)

<template>
  <div>Your customized greeting: {{ greeting }}</div>
</template>
<script>
  import createEffect from 'ceci-hook';

  const noop = () => {};

  export default {
    name: 'EffectExample',
    
    props: {
      name: String,
    },
    
    data() {
      return {
        name: null,
      };
    },
    
    created() {
      // Create the almost-hook.
      this._fetchGreeting = createEffect((name) => {
        // `name` is set to `null` on component unmount, so don't do any setup and
        // don't return a cleanup callback either.
        if (!name) return null;
    
        let setGreeting = (greeting) => {
          this.greeting = greeting;
        };
        fetch(`/api/greet?username=${name}`)
          .then((response) => response.text())
          .then((greeting) => setGreeting(greeting));
    
        return () => {
          // Low-effort way to prevent a race condition.
          setGreeting = noop;
        }
      });
      
      // Call the almost-hook with the initial value, so that
      // the greeting is displayed as soon as possible.
      this._fetchGreeting(this.name);
    },
    
    beforeUpdate() {
      // Update our almost-hook. Note that no conditional logic is
      // required because `ceci-hook` only runs your side-effect if
      // its arguments have changed.
      this._fetchGreeting(this.name);
    },
    
    beforeDestroy() {
      // Run cleanup, in case a network call was in progress
      // when the component was unmounted.
      this._fetchGreeting(null);
    },
  };
</script>