Giter Club home page Giter Club logo

reuse's Introduction



reuse


Reuse different React components to create new ones
Play on CodeSandbox



NPM version Dependencies Build Status Coverage Status

Installation

npm i reuse

Thanks to @eldargab for the package name on npm.

Why

This enables (sub)atomic design approach.

When using classic CSS, we have a powerful way to compose "stylesheet components" by applying multiple class names to our HTML elements (.btn, .large, .rounded etc.). But, by doing that in React, which has its own component structure, we'll have conflicting component structures.

Reuse solves it by combining React components together as if they were CSS classes. This also means that not only style will be composed, but also JavaScript behavior, like React lifecycle methods and event handlers.

Usage

Reuse simply exports a factory method that returns a React component. You can leverage that method in two ways: augmentation and combination.

Examples

Augmentation

The component returned by the use factory will expect a use prop:

import use from "reuse";

const Box = use();

<Box />; // null
<Box use="div" />; // <div />
<Box use={Link} />; // <Link />

You can create the component with a default element:

const Box = use("div");

<Box />; // <div />
<Box use="span" />; // <span />

You can create the component with another component. Just make sure to render the use prop as the underlying element and pass the other props down (at least, when use isn't a string – HTML element):

import React from "react";
import use from "reuse";

// grab the `use` prop and pass down other props
const Base = ({ use: T = "div", ...props }) => <T {...props} />;

const Box = use(Base);

<Box />; // <div />
<Box use="span" />; // <span />

const BoxSpan = use(Box, "span");
<BoxSpan />; // <span />

You can use Base to filter custom props when use is a string using @emotion/is-prop-valid, for example.

Combination

Let's create some components:

// Using styled-components
const Paper = styled(use("div"))`
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.30);
`;

// Using class names
const Rounded = use(({ use: T, ...props }) => (
  <T
    {...props}
    className={`rounded ${props.className}`}
  />
), "div");

// Using inline styles
const Button = use(({ use: T, ...props }) => (
  <T
    {...props}
    style={{
      padding: "0 1em",
      lineHeight: "2.5em",
      background: "#3f51b5",
      color: "white",
      ...props.style
    }}
  />
), "button");

Once you have a few of those components, you can combine them using the same use methods:

import use from "reuse";
import { Rounded, Paper, Button } from "../components";

// with factory
const RoundedPaperButton = use(Rounded, Paper, Button);
<RoundedPaperButton />; // <button style="..." class="..." />
<RoundedPaperButton use="div" />; // <div style="..." class="..." />

// with prop
<Rounded use={[Paper, Button]} /> // <button style="..." class="..." />
<Rounded use={[Paper, Button, "div"]} /> // <div style="..." class="..." />

Note that the underlying HTML element will always be based on the last component you pass to use.

FAQ

How does this compare to render props and HOCs?

These are equivalent implementations:

Render props

<Paper>
  {paperProps => (
    <Rounded {...paperProps}>
      {roundedProps => (
        <Button {...roundedProps}>
          {buttonProps => (
            <button {...buttonProps}>Button</button>
          )}
        </Button>
      )}
    </Rounded>
  )}
</Paper>

High-order components

withPaper(withRounded(withButton(props => <button {...props}>Button</button>)));

Reuse

use(Paper, Rounded, Button);
// or
<Paper use={[Rounded, Button]} />

When using render props or HOCs, you have to stick with their static (HOC) or dynamic implementation (render prop). With Reuse, besides simplicity, you can use both depending on your needs.

License

MIT © Haz

reuse's People

Contributors

diegohaz avatar jacobwgillespie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

reuse's Issues

Consider exposing a selection helper.

Using an example from the reakit beta, let's say I want to make a button link

<Button to="/somewhere" use={Link} />

This works, but it has the unexpected side effect of passing all of the props Button uses to Link. Unexpected attributes are written to the DOM and you'll get a warning for trying to write the boolean opaque. It's not impossible to get around this

const SelectiveLink = props => <Link className={props.className} to={props.to} />
// ...
<Button to="/somewhere" use={SelectiveLink} />

but it has the side effect of adding another component to the tree. I think a better approach would be to allow the user to provide a list of props they want, then use that during the render over omit.
I propose an API like

import {select} from 'reuse';

// So instead of `omit(props, 'use', 'useNext')`, it'll do `pick(props, 'className', 'to')`.
const SelectiveLink = select(Link, ['className', 'to']);
// ...
<Button to="/somewhere" use={SelectiveLink} />

It wouldn't supplant the current one, just add a hot path and a helper. A potentially less verbose option would be an omission helper

import {filter} from 'reuse';

// So it'll do `omit(props, 'opaque', 'palette', 'use', 'useNext')`.
const SelectiveLink = filter(Link, ['opaque', 'palette']);
// ...
<Button to="/somewhere" use={SelectiveLink} />

but this is also potentially less performant. Additionally, the list of props could be provided by the implementing component.

const SelectiveLink = filter(Link, Button.propNames);

injecting props into a reuse component..

Probably it's not even a reuse issue but my undeveloped understanding of some of the general logic behind it:
If I want to use, for example, a hook, that takes a reuse/styled-components element as an argument and injects some event handler via clone.children into the element, it does not work with reuse.
What's the missing piece of the puzzle? (:

styles composition with styled-components@4

Hello @diegohaz!

I've realized that contrary to my first impression, reuse is not aware of styled-components@4 component extension feature. That makes predictable overriding of styles impossible, as the order of generated classes depends on component declaration/import order.

This might be out of scope for reuse if CSS properties of combined components are considered disjunct by convention, which might be a good idea.

I still see a lot of potential in this so I took a quick hack at that here https://github.com/brabeji/styled-compose

What do you think?

Babel plugin for reuse?

I think having a babel plugin will make using this library even more silky smooth, like

"plugins": [
  ["babel-plugin-reuse", [{
    "prop": "as"
  }]]
]

That way even primitives like h1 can use reuse right in jsx like

<h1 as={Heading}/>

I am trying to make it, though I don't know much about making babel plugins 🤔

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.