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

proppy

v1.3.1

Published

ProppyJS - Functional composition of props for components

Downloads

78

Readme

proppy

npm

Functional props composition for components


Guide

Installation

With npm:

$ npm install --save proppy

Via unpkg CDN:

<script src="https://unpkg.com/proppy@latest/dist/proppy.min.js"></script>

<script>
  // available as `window.Proppy`
</script>

Usage

Single functions

Let's first import a function of our choice:

import { withState } from 'proppy';

Now we can create our factory:

const P = withState('counter', 'setCounter', 0);

The factory function can now be called to get the instance:

const p = P();

Accessing props:

console.log(p.props);
// { counter: 0, setCounter: Function }

p.props.setCounter(5);

console.log(p.props.counter);
// 5

Destroying the instance:

p.destroy();

Composition

You can also compose multiple factories together using the compose function:

import { compose, withProps, withState } from 'proppy';

const P = compose(
  withProps({ foo: 'foo value' }),
  withState('counter', 'setCounter', 0),
  withState('age', 'setAge', 20),
);

You can now get your instance by calling the returned function:

const p = P();

console.log(p.props);
// {
//   foo: 'foo value',
//
//   counter: 0,
//   setCounter: Function,
//
//   age: 20,
//   setAge: Function
// }

Providers

Providers are values which are made accessible to instances.

When we call our factory function to get the instance, we can optionally pass an object that will contain all our providers:

import { compose, withProps } from 'proppy';

const P = compose(
  // value already known
  withprops({
    foo: 'foo value',
  }),

  // value will be obtained from providers
  withProps((props, providers) => ({
    // `props` is `{ foo: 'foo value' }` here
    bar: providers.bar,
  }),
);

Now we can pass the providers object when calling P:

const p = P({
  bar: 'bar value',
});

console.log(p.props);
// {
//   foo: 'foo value',
//   bar: 'bar value',
// }

Subscription

Subscribing to props changes:

const unsubscribe = p.subscribe(props => console.log(props));

To Unsubscribe:

unsubscribe();

API

withProps

withProps(props)

withProps((currentProps, providers) => props)

Used for generating initial props.

Examples:

Directly passing the props object:

const P = withProps({ foo: 'foo value' });

Returning the props from a function:

const P = withProps((currentProps, providers) => {
  return {
    foo: 'foo value'
  };
});

withState

withState(stateName, setterName, initialState)

Used for state management.

Examples:

const P = withState('counter', 'setCounter', 0);

compose

compose(...factories)

Used for composing multiple factories into a single one.

Examples:

const P = compose(
  withProps({ foo: 'foo value '}),
  withprops({ bar: 'bar value' }),
  withState('counter', 'setCounter', 0),
);

withReducer

withReducer(stateName, dispatcherNAme, reducer, initialState)

For generating props via reducer functions.

Arguments:

  1. stateName (String): Name of the prop where state will be set
  2. dispatcherName (String): Name of the prop for setting dispatch function
  3. reducer (Function): Accepting (state, action), and returning new state
  4. initialState (Any): Initial state to begin with

Example:

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { value: state.value + 1 };
    case 'DECREMENT':
      return { value: state.value - 1 };
    default:
      return state;
  }
}

const P = withReducer('counter', 'dispatch', reducer, { value: 0 });
const p = P();

p.dispatch({ type: 'INCREMENT' });
console.log(p.props.counter) // { value: 1 }

You can also pass functions to dispatch:

p.dispatch(function (props, providers) {
  return { type: 'INCREMENT' };
});

withHandlers

withHandlers(handlers)

Used for handling side-effects.

Examples:

Basic handler:

const P = withHandlers({
  handleFoo: (props, providers) => () => console.log('handling foo!')
});

A better example with composition:

const P = compose(
  withState('counter', 'setCounter', 0),
  withHandlers({
    incrementBy: (props, providers) => (n) => {
      props.setCounter(props.counter + n);
    },
  }),
);

const p = P();

p.incrementBy(1);
console.log(p.props.counter); // 1

withStateHandlers

withStateHandlers(initialState, handlers)

Used for state management via handlers.

Examples:

const P = withStateHandlers(
  // initial state
  {
    counter: 0,
  },

  // handlers
  {
    incrementBy: (props, providers) => (n) => {
      // return new state
      return {
        counter: props.counter + n,
      };
    }
  }
);

const p = P();
p.incrementBy(5);
p.incrementBy(5);
console.log(p.props.counter); // 10

withObservable

withObservable((props, providers) => props$)

Used for returning an Observable of props.

Examples:

import { of } from 'rxjs';

const P = withObservable((props, providers) => {
  return of({
    foo: 'foo value',
  });
});

For advanced usage, look into proppy-rx package, where you can also access incoming props as an Observable.

withTimer

withTimer(timer, props)

withTimer(timer, (props, providers) => props)

Sets props after timer has elapsed.

Examples:

// prop `foo` is set after 1 second
const P = withTimer(1000, { foo: 'foo value' });

Accessing current props and providers:

const P = withTimer(100, (props, providers) => ({
  foo: 'foo value',
}));

onChange

onChange(propName, (prop, providers) => props)

onChange( (prevProps, nextProps) => true, (props, providers) => props )

onChange( propName, (props, providers, cb) => void )

Detect a change in props, and return new props.

Examples:

Change foo, when counter changes:

const P = compose(
  withProps({ foo: 'initial foo value' })
  withState('counter', 'setCounter', 0),
  onChange('counter', (props, providers) => ({
    foo: `changed foo with counter ${props.counter}`
  }))
);

const p = P();
console.log(p.props.foo); // `initial foo value`

p.props.setCounter(10);
console.log(p.props.foo); // `changed foo with counter 10`

Detecting complex changes:

const P = compose(
  withState('counter', 'setCounter', 0),
  onChange(
    // detect
    (prevProps, nextProps) => true,

    // return props
    (props, providers) => props
  )
);

Returning async props:

const P = compose(
  withState('counter', 'setCounter', 0),
  onChange(
    // detect
    (prevProps, nextProps) => true,

    // return props by callback
    (props, providers, cb) => {
      cb(props);
    }
  )
);

map

map((props, providers) => props)

Used for mapping incoming props to returned props.

Examples:

const P = compose(
  withProps({ foo: 'foo value' }),
  map(props => ({ foo: props.foo.toUpperCase() })),
);

shouldUpdate

shouldUpdate((prevProps, nextProps, providers) => true)

Used for limiting the events

Examples:

const P = compose(
  withState('counter', 'setCounter', 0),
  shouldUpdate((prevProps, nextProps) => {
    // emit further only if counter is an even number
    return nextProps.counter % 2 === 0;
  });
);

didSubscribe

didSubscribe((props, providers) => {})

For handling side-effects upon first subscription.

Examples:

const P = didSubscribe((props, providers) => {
  // do something

  // optionally return a function
  // that will be called when instance is destroyed
  return () => {};
})

willDestroy

willDestroy((props, providers) => {})

For handling side-effects when instance is destroyed.

emit

emit((cb, props, providers) => {})

For emitting new props after subscription.

Examples:

const P = emit((cb, props, providers) => {
  cb({ foo: 'foo value' });

  // optionally return a function
  // that will be called when instance is destroyed
  return () => {};
});

handleReceivedProps

handleReceivedProps(true|false)

handleReceivedProps((receivedProps) => {})

Accept props coming from parent instances.

create

create(options)

Convenience function for creating your own functions, that can return factories.

All the other functions in this library are built by using this create function internally.

Arguments:

  • options.initialize (Function)
  • options.didSubscribe (Function)
  • options.willDestroy (Function)
  • options.handleReceivedProps (Boolean|Function)

Examples:

A producer of props:

function withFoo() {
  return create({
    initialize() {
      // has access to:
      //   - this.props
      //   - this.providers

      this.set({
        foo: 'foo value',
      });
    }
  });
}

A mapper of props:

function capitalizeFoo() {
  return create({
    handleReceivedProps(receivedProps) {
      this.set({
        ...receivedProps,
        foo: typeof receivedProps.foo !== 'undefined'
          ? receivedProps.foo.toUpperCase()
          : undefined,
      })
    }
  });
}