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

precompose-props

v0.0.4

Published

Prop mapping made easy.

Downloads

8

Readme

Precompose-props

Prop mapping made easy.

travis npm

Table of Contents

Install

This package is distributed via npm.

$ npm install --save precompose-props
# or
$ yarn add precompose-props

Then import according to your modules model and bundler, such as Rollup and Webpack

// ES6 Modules
// For all possible functions to import, look at "export" in src/index.js
import { contramap, withTheme } from 'precompose-props';

/// CommonJS modules
var precomposeProps = require('precompose-props');

A UMD version is also available on unpkg:

<script src="https://unpkg.com/precompose-props/dist/precompose-props.umd.js"></script>

Motivation

When working with React, I often want to make a component that "wraps" the props of another component, mapping from one set to another. This is especially true when composing styles; I want to have a higher-level "theme" prop, that collects a set of lower-level "style" props. Doing this manually has some annoying boilerplate, and leads to more noise than I'd like.

Ideally, I would be able to do something like this:

const Th = ({fontWeight, color, children}) => <th style={{fontWeight, color}}>{children}</th>;

const Cell = withTheme({bold: {fontWeight: '7', color: 'black'}})(Th);

Ideas

In his talk "Oh Composable World!", Brian Lonsdorf (aka Dr. Boolean) goes over how to use contramap to compose props. A contramap in this context allows us to use a function pre-process a set of props, before passing the result on to a component. This is exactly what we want! (Also, that sounds like mapStateToProps() from react-redux).

Let's define contramap:

const contramap = mapFn => Component => props => Component(mapFn(props));

In his talk, he defines a Comp = x => {...} function, because there is a larger point about composition and concatenation for React (contravariance and monoids). I am not doing that, because calling .fold() would look out of place in our codebase. Would be simple to add and fun to try though :)

Usage

import React from "react";
import { render } from "react-dom";
import {
  withTheme,
  named,
  toggle,
  contramap,
  concatAndMergeProps
} from "precompose-props";

// The lower component
const P = ({ measure, lineHeight, fontSize, fontWeight, children }) => (
  <p style={{ maxWidth: measure, lineHeight, fontWeight, fontSize }}>
    {children}
  </p>
);

// Higher component
const Text = withTheme({
  bold: toggle({ fontWeight: "700" }),
  kind: named(
    { copy: { lineHeight: "1.5", measure: "34em" } },
    { heading: { lineHeight: "1.25" } }
  )
})(P);

// Equivalent forms
// Using concatAndMergeProps directly with contramap
const TextAlt = contramap(
  concatAndMergeProps({
    bold: toggle({ fontWeight: "700" }),
    kind: named(
      { copy: { lineHeight: "1.5", measure: "34em" } },
      { heading: { lineHeight: "1.25" } }
    )
  })
)(P);

// Using contramap with a custom function
/*
mapFakeToProps(({bold, kind, ...rest}) => "Fill this in");
const TextManual = contramap(mapFakeToProps)(P);
*/

const App = () => (
  <div>
    <Text kind="heading" fontSize="2rem" bold>
      I am a Heading
    </Text>
    <Text kind="copy" fontSize="1rem">
      This is a text oh this is a text, and would you guess, this is copy text
      with max widths and stuff.
    </Text>
    <TextAlt kind="copy" fontSize="1rem">
      This is a text oh this is a text, and would you guess, this is copy text
      with max widths and stuff.
    </TextAlt>
  </div>
);

render(<App />, document.getElementById("root"));

Any abstraction you like

Another piece of wisdom by Dr. Boolean (I'm a fan, can't you tell?) is on API design (paraphrasing):

  • A set of primitives
  • A way to compose them
  • A set of precomposed things

Armed with that thought, here are the functions provided and their use cases, lower- to higher- level.

Low-level

  • contramap apply a mapping function from one set of props to another, pass to component

  • concatProps concatenate props according to a specification

  • concatAndMergeProps concatenate props according to a specification, merges

Specification Utilities

A specification is of the form

{[K in higherProp]: mapFn}

mapFn: higherPropValue => Partial<LowerProps>`.
  • toggle(obj)(value) return obj if value is true

  • named(obj)(value) return the entry at obj[value]

Higher-level

  • withTheme maps props with specification, merge other props

  • mapTheme maps props with specification, do not merge other props

If you see a pattern there, it's because the latter are simply compositions of the former! Functions are curried by default. I have not used Ramda's curry; this is open to change.

Examples

Edit the example above on CodeSandbox

API

Table of Contents

TODO

  • Types
  • Example folder

Inspiration

  • Brian Lonsdorf (drboolean) for his talks on functional JS. Even when the language is not the best for it, I will occasionally run into cases where I think about concatenation or the order of operations.
  • Jason Miller (developit) for his package build setups, and microbundle. They were a great starting point to figure out how to publish this damn thing.