ProppyJS

Tiny 1.5kB utility library enabling

Functional props composition for components

Learn more Quick start

Flow of props

Various sources

  • Redux

    Redux

  • JavaScript

    JavaScript

  • RxJS

    RxJS

Compose as props

ProppyJS

Proppy

Pass to any Component

  • React

    React

  • Vue

    Vue.js

  • Preact

    Preact

Examples

import React from 'react';
import { compose, withProps, withState } from 'proppy';
import { attach } from 'proppy-react';

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

function MyComponent({ foo, counter, setCounter }) {
  return (
    <div>
      <p>Foo: {foo}</p>

      <p>Counter: {counter}</p>

      <button onClick={() => setCounter(counter + 1)}>
        Increment
      </button>
    </div>
  );
}

export default attach(P)(MyComponent);
import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      foo: 'foo value',
      counter: 0
    };

    this.handleIncrement = this.handleIncrement.bind(this);
  }

  handleIncrement() {
    const currentCounter = this.state.counter;

    this.setState({
      counter: currentCounter + 1
    });
  }

  render() {
    const { foo, counter } = this.state;

    return (
      <div>
        <p>Foo: {foo}</p>

        <p>Counter: {counter}</p>

        <button onClick={this.handleIncrement}>
          Increment
        </button>
      </div>
    );
  }
}

export default MyComponent;

See detailed example here.

import { compose, withProps, withState } from 'proppy';
import { attach } from 'proppy-vue';

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

const MyComponent = {
  props: ['foo', 'counter', 'setCounter'],

  render(h) {
    const { foo, counter, setCounter } = this;

    return (
      <div>
        <p>Foo: {foo}</p>

        <p>Counter: {counter}</p>

        <button onClick={() => setCounter(counter + 1)}>
          Increment
        </button>
      </div>
    );
  },
}

export default attach(P)(MyComponent);
const MyComponent = {
  data: function () {
    return {
      counter: 0
    };
  },

  methods: {
    handleIncrement: function () {
      const currentCounter = this.counter;

      this.counter = currentCounter + 1;
    }
  },

  render(h) {
    return (
      <div>
        <p>Counter: {this.counter}</p>

        <button onClick={this.handleIncrement}>
          Increment
        </button>
      </div>
    );
  }
};

export default MyComponent;

See detailed example here.

import { h } from 'preact';
import { compose, withProps, withState } from 'proppy';
import { attach } from 'proppy-preact';

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

function MyComponent({ foo, counter, setCounter }) {
  return (
    <div>
      <p>Foo: {foo}</p>

      <p>Counter: {counter}</p>

      <button onClick={() => setCounter(counter + 1)}>
        Increment
      </button>
    </div>
  );
}

export default attach(P)(MyComponent);
import { h } from 'preact';

class MyComponent extends Preact.Component {
  constructor(props) {
    super(props);

    this.state = {
      foo: 'foo value',
      counter: 0
    };

    this.handleIncrement = this.handleIncrement.bind(this);
  }

  handleIncrement() {
    const currentCounter = this.state.counter;

    this.setState({
      counter: currentCounter + 1
    });
  }

  render(props, state) {
    const { foo, counter } = state;

    return (
      <div>
        <p>Foo: {foo}</p>

        <p>Counter: {counter}</p>

        <button onClick={this.handleIncrement}>
          Increment
        </button>
      </div>
    );
  }
}

export default MyComponent;

See further guides here.

import React from 'react';
import { withStore } from 'proppy-redux';
import { attach } from 'proppy-react';

import { incrementCounter } from '../actions/counter';

const P = withStore(
  state => ({ counter: state.counter.value }),
  { increment: incrementCounter }
);

function MyComponent({ counter, increment }) {
  return (
    <div>
      <p>Counter: {counter}</p>

      <button onClick={increment}>
        Increment
      </button>
    </div>
  );
}

export default attach(P)(MyComponent);
import React from 'react';
import { connect } from 'react-redux';

import { incrementCounter } from '../actions/counter';

function mapState(state) {
  return {
    counter: state.counter.value
  };
}

const mapDispatch = {
  increment: incrementCounter
};

function MyComponent({ counter, increment }) {
  return (
    <div>
      <p>Counter: {counter}</p>

      <button onClick={increment}>
        Increment
      </button>
    </div>
  );
}

export default connect(mapState, mapDispatch)(MyComponent);

See detailed example here.

import React from 'react';

import { interval } from 'rxjs';
import { map } from 'rxjs/operators';

import { withStream } from 'proppy-rx';
import { attach } from 'proppy-react';

const P = withStream(incomingProps$ => {
  return interval(1000).pipe(
    map(n => ({ interval: n }))
  );
});

function MyComponent({ interval }) {
  return <p>Interval: {interval}</p>;
}

export default attach(P)(MyComponent);
import React from 'react';

import { interval } from 'rxjs';
import { map } from 'rxjs/operators';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      interval: 0
    };
  }

  componentDidMount() {
    const interval$ = interval(1000).pipe(
      map(n => ({ interval: n }))
    );

    this._rxSubscription = $interval.subscribe(intervalProps => {
      this.setState(intervalProps);
    });
  }

  componentWillUnmount() {
    this._rxSubscription.unsubscribe();
  }

  render() {
    const { interval } = this.props;

    return <p>Interval: {interval}</p>;
  }
}

export default MyComponent;

See detailed example here.

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

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

const p = P();

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

p.setCounter(5);

console.log(p.props);
// {
//   foo: 'foo value',
//   counter: 5,
//   setCounter: Function
// }

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

unsubscribe();
// without proppy

See quick start guide here.

Benefits

Stateless

Your component layer ends up becoming stateless, and only responsible for accepting props and rendering them.

Interoperable

Integrating other libraries to your components layer becomes a breeze with the suite of functions that Proppy provides you.

Functional

With your props being composed in functions, they become easier to expand as your requirements grow.

Testing

With clear separation between props generation and components, you can now unit test them separately with ease.

Providers

With Proppy's providers, you can set application-wide global object accessible anywhere in your components tree.

Freedom

Since Proppy connects to your favourite UI rendering library, you have the freedom to switch with minimal effort.

Sounds good? Let's get started!