GithubHelp home page GithubHelp logo

renatorib / react-powerplug Goto Github PK

View Code? Open in Web Editor NEW
2.7K 27.0 102.0 1.33 MB

:electric_plug: [Unmaintained] Renderless Containers

Home Page: https://renatorib.github.io/react-powerplug

License: MIT License

JavaScript 89.10% TypeScript 10.90%
dumb-components dumb state react react-component javascript renderless renderless-components state-container render-props

react-powerplug's Introduction

React PowerPlug

npm stars tweet


React PowerPlug is a set of pluggable renderless components and helpers that provides different types of state and logic utilities that you can use with your dumb components. It creates state and passes down the logic to the children, so you can handle your data. Read about the Render Props pattern.

Highlights

  • ๐Ÿ‘Œ Dependency free
  • ๐Ÿ”Œ Plug and play
  • ๐Ÿ”ฎ Tree shaking friendly (ESM, no side effects)
  • ๐Ÿ“ฆ Super tiny (~3kb)
  • ๐Ÿ“š Well documented
  • ๐Ÿป Bunch of awesome utilities
See quick examples
import { State, Toggle } from 'react-powerplug'
import { Pagination, Tabs, Checkbox } from './MyDumbComponents'

<State initial={{ offset: 0, limit: 10, totalCount: 200 }}>
  {({ state, setState }) => (
    <Pagination {...state} onChange={(offset) => setState({ offset })} />
  )}
</State>

<Toggle initial={true}>
  {({ on, toggle }) => (
    <Checkbox checked={on} onChange={toggle} />
  )}
</Toggle>

// You can also use a `render` prop instead

<Toggle
  initial={false}
  render={({ on, toggle }) => (
    <Checkbox checked={on} onChange={toggle} />
  )}
/>

Guide & Documentation

http://rena.to/react-powerplug/


Watch 'Rapid Prototyping with React PowerPlug' by Andrew Del Prete on egghead.io


Install

Node Module

yarn add react-powerplug
npm i react-powerplug

UMD

<script src="https://unpkg.com/react-powerplug/dist/react-powerplug.min.js"></script>

exposed as ReactPowerPlug

Contributors

Thanks goes to these wonderful people (emoji key):


Renato Ribeiro

๐Ÿ’ป ๐ŸŽจ ๐Ÿ“– โš ๏ธ

Bogdan Chadkin

๐Ÿ’ป ๐Ÿ“– โš ๏ธ ๐Ÿš‡

Travis Arnold

๐Ÿ’ป ๐Ÿ“– ๐Ÿ›

Max Graey

๐Ÿ’ป

Mateusz Burzyล„ski

๐Ÿ›

Andy Edwards

๐Ÿ’ป

Andrea Vanini

๐Ÿ›

Ivan Starkov

๐Ÿ›

Sean Roberts

๐Ÿ“–

Braden Kelley

๐Ÿ›

This project follows the all-contributors specification. Contributions of any kind welcome!

Contribute

You can help improving this project sending PRs and helping with issues.
Also you can ping me at Twitter

react-powerplug's People

Contributors

andarist avatar apuntovanini avatar baterson avatar beizhedenglong avatar coder054 avatar cramhead avatar denisborovikov avatar egoarka avatar everdimension avatar farskid avatar gnapse avatar istarkov avatar jaaberg avatar jedwards1211 avatar jeetiss avatar kpman avatar linusu avatar maxgraey avatar renatorib avatar rfviolato avatar rvcas avatar seanroberts avatar shoaibbhimani avatar slorber avatar souporserious avatar supernova23 avatar thekashey avatar trysound avatar txhawks avatar yaireo 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-powerplug's Issues

Form values are empty

First of all: Thanks, @renatorib for building react-powerplug. It is a masterpiece!

I found a tricky scenario when using powerplug with react-adopt though. Curious to hear your thoughts on this. Not sure if this is really a bug. Consider the following example:

import React from "react";
import { render } from "react-dom";

import { adopt } from "react-adopt";
import { Form } from "react-powerplug";

const Container = adopt({
  form: <Form initial={{ username: "", password: "" }} />
});

const App = () => (
  <Container>
    {({ form }) => {
      return (
        <form
          onSubmit={e => {
            e.preventDefault();

            console.log(form.input("username").value);
            console.log(form.input("password").value);

            console.log("----");
            console.log(form.values);
          }}
        >
          <p>
            username
            <input type="text" {...form.input("username").bind} />
          </p>
          <p>
            password
            <input type="password" {...form.input("password").bind} />
          </p>

          <button type="submit">Login</button>
        </form>
      );
    }}
  </Container>
);

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

The console.log(form.values); is always undefined, whereas printing the inputs separately works like a charm.

webpack doesn't seem to be able to tree-shake the rollup bundle, I analyzed some webpack builds...

I think we need to do some more investigation of this. I profiled my webpack production build after adding the following single import:

import {Toggle} from 'react-powerplug'

As seen in the webpack analyze tool, All 22 KiB of react-powerplug got bundled up: (EDIT: I need to check if this is actually the size of the code in the bundle or just the input file size. It's not clear from the webpack analyze tool...)

image

Then I added "sideEffects": false to node_modules/react-powerplug/package.json, reran the build, and analyzed again. The result? Unfortunately, still 22 KiB.

Also, while trying to see if I could successfully tree shake version 0.1.6 instead, I noticed that the es/ directory contained commonjs exports, which I thought defeats the purpose of having a separate es/ directory?

Warning: The prop `children` is marked as required

When I try to compose components with PropTypes.children.isRequired then I see this annoying warning.

screen shot 2018-08-05 at 8 36 36 pm

The temporary solution is not looking good:

<Compose
    components={[
        <Query query={QUERY} children={() => {}} />,
        <Mutation mutation={MUTATION} children={() => {}} />,
    ]}
>

Export flow type definitions

Would it be possible to export all flow type definitions? Here's a use case:

import { Value } from 'react-powerplug';
import { adopt } from 'react-adopt';

const Composed = adopt({
    foo: <Value initial="bar" />,
    baz: <Value initial="qux" />
});

This will make flow complain:

Cannot create Value element because:
 โ€ข Either property render is missing in props [1] but exists in object type [2].
 โ€ข Or property children is missing in props [1] but exists in object type [3].

 [2] 274โ”‚   | {| initial: T, onChange?: ValueChange<T>, render: ValueRender<T> |}
 [3] 275โ”‚   | {| initial: T, onChange?: ValueChange<T>, children: ValueRender<T> |}

Which makes sense I guess. This can be fixed by pulling out the render explicitly:

import { Value } from 'react-powerplug';
import { adopt } from 'react-adopt';

const Composed = adopt({
    foo: ({ render }) => <Value initial="bar">{render}</Value>,
    baz: ({ render }) => <Value initial="qux">{render}</Value>,
});

But now, I have to type render (at least with the react/prop-types eslint-rule active).

Sadly, react-powerplug does not currently export the ValueRender type which makes this hard to do... Could you export all flowtypes?

Can not get alpha to work

Hello,

I am testing the alpha release and can not get it to work I am always getting:
Warning: State(...): No render method found on the returned component instance: you may have forgotten to define render.

Rename some component in 1.0

  • Set conflicts with native one
  • State AFAIK is used often with typescript and flow for state type (it's still may be required for State component initial).

It's just a note to consideration.

onChange bubbling

Having code:

<Value>
{ ({value, set}) =>
  <Value onChange={set}>
  { ({set} ) =>...
 </Value>
}
</Value>

When I will call set on internal Value it will set that value, and call onChange handler.
onChange handler will be called with {value}, not value, as long onChange is a State property.

Result: value on topmost Value will be {value}.

Proposal: onChange on any component should emit value of that component unchanged. As it will be presented to the consumer.

[feature] boolean checkbox support in <Form />

more complete Form?

import * as React from 'react'
import State from './State'
import renderProps from '../utils/renderProps'
import set from '../utils/set'

const Form = ({ initial = {}, onChange, ...props }) => (
  <State initial={{ ...initial }} onChange={onChange}>
    {({ state, setState }) =>
      renderProps(props, {
        values: { ...state },
        input: id => {
          const value = state[id] || ''
          const setValue = value => setState({ [id]: value })

          return {
            bind: {
              onChange: event => setValue(event.target.value),
              value,
            },
            set: value => setState(s => ({ [id]: set(value, s.value) })),
            value,
          }
        },
        checkbox: id => {
          const checked = state[id] || ''
          const setValue = checked => setState({ [id]: checked })

          return {
            bind: {
              onChange: event => setValue(event.target.checked),
              checked,
            },
            set: checked => setState(s => ({ [id]: set(checked, s.checked) })),
            value,
          }
        }
      })
    }
  </State>
)

export default Form

or even a breaking change

renderProps(props, {
  values: { ...state },
  // the difference between value and checked is just `event.target.value/checked`, so `select` use `value`
  value: id => {
    const value = state[id] || ''
    const setValue = value => setState({ [id]: value })

    return {
      bind: {
        onChange: event => setValue(event.target.value),
        value,
      },
      set: value => setState(s => ({ [id]: set(value, s.value) })),
      value,
    }
  },
  checked: id => {
    const checked = state[id] || ''
    const setValue = checked => setState({ [id]: checked })

    return {
      bind: {
        onChange: event => setValue(event.target.checked),
        checked,
      },
      set: checked => setState(s => ({ [id]: set(checked, s.checked) })),
      value,
    }
  }
})

onMount={props => ...}

I'm trying to use powerplug in a docz Playground, where components can only be instanciated without carrying state. I would like to run a couple of actions once the component has mounted. It would generally be nice if powerplug components had simple to use mounting semantics where the same props that are otherwise accessible in render could be used to drive async actions, receive stuff, etc.

My specific problem:

<Playground>
  <List initial={['Apples', 'Oranges', 'Bananas']}>
    {({ list, push, pull }) => (

      // I'd like to call setTimeouts on startup, removing an item on the first,
      // adding items on the second, can't do it here coz it would cause a loop

      <Transition
        keys={list}
        from={{ overflow: 'hidden', height: 0 }}
        enter={{ height: 30, background: '#28d79f' }}
        leave={{ height: 0, background: '#c23369' }}
        update={{ background: '#28b4d7' }}
        config={config.molasses}>
        {list.map(item => props => <div style={props}>{item}</div>)}
      </Transition>
    )}
  </List>
</Playground>

A possible solution:

<Playground>
  <List 
    initial={['Apples', 'Oranges', 'Bananas']}
    onMount={async ({ push, pull }) => {
      await delay(1000)
      pull(item => item === 'Oranges')
      await delay(1000)
      push('Kiwis')
    }}>
    {({ list }) => (
      <Transition
        keys={list}
        from={{ overflow: 'hidden', height: 0 }}
        enter={{ height: 30, background: '#28d79f' }}
        leave={{ height: 0, background: '#c23369' }}
        update={{ background: '#28b4d7' }}
        config={config.molasses}>
        {list.map(item => props => <div style={props}>{item}</div>)}
      </Transition>
    )}
  </List>
</Playground>

[Feature Request] <Tabs /> component for Tabs or Carousel.

Hello, first, thank you for this wonderful library!
I want to ask if we can have a component for Tabs or Carousel usage.
For example:

<Tabs items={['A', 'B', 'C']} infinite>
{
  ({currentTab, inc, dec}) => {
    return (
      <div className='some tabs or carousel'>
        Current position is ${currentTab}
      </div>
    )
  }
}
</Tabs>

Infinite prop is if I am in C, and I trigger inc(), I will be A.
Thank you!

[Feature Request] Integrate with HOCs.

Hello, first of all, thank you for this wonderful library.
This library and react-fns are my favorite librarys!
However, it's often to run into some situations that you need to access some property in lifecycle hoods that react-powerplug provides you.
If we can have HOC, the code will become much nicer like this.

export default withToggle(MyApp)

Thank you!

Individual imports

First, great job on everything! I love all of these small, powerful components ๐Ÿ™Œ. I was curious.. since I only need a subset of these, is it possible to set up the build so we can do imports like this:

import Hover from 'react-powerplug/Hover'

I'm not too familiar with rollup, but I can try out a PR if you would like ๐Ÿ˜

PowerPlug Website

This issue will serve to discuss the powerplug website. It will only be closed when we launch it.

Discussion for 1.0

1.x will be a fully breaking change in 0.x api, and that's why I'm planning on version 1.0 so early.

I'm open to all suggestions, opinions, considerations, etc.

Components and their Children Props

State
  state
  setState

Toggle
  on
  toggle
  set

Counter
  count
  inc
  dec
  incBy
  decBy
  set

List
  list
  push
  remove
  filter
  sort
  set

Map (KeyValue?)
  values
  set
  get

Set
  values
  add
  clear
  remove
  has

Value
  value
  set

Input
  bind
  value
  set
  
Form
  input(key)
    bind
    value
    set

Hover
  isHover
  bind

Active
  isActived
  bind

Focus
  isFocused
  bind

Touch
  isTouched
  bind

Compose

Set Methods

All set methods will be the "set or over pattern" (I don't know if this pattern have a name), same as setState: If pass plain value, set the value, if pass function, evaluate with actual value and set the value.

function set(value: ValueType | ((value: ValueType) => ValueType): void
set(15)  // raw set
set(value => value + 5) // set using prev value

Example:

<Value initial={1}>
  {({ value, set }) => (
    <div>
       Your value is: {value}<br />
       <button onClick={() => set(0)}>Reset</button>
       <button onClick={() => set(10)}>Set value to 10</button>
       <button onClick={() => set(v => v + 10)}>Add 10</button>
    </div>
  )}
</Value>

Compose

Compose function will wrap the component methods/properties in a object with their lowercase name. It avoids naming conflicts like set, value, etc. Example:

const CounterToggle = compose(Toggle, Counter)

Before (0.x):

<CounterToggle>
  {({ count, inc, dec, toggle, on }) => (
    <div onClick={inc}>...</div>
  )}
</CounterToggle>

After (1.x):

<CounterToggle>
  {({ counter, toggle }) => (
    <div onClick={counter.inc}>...</div>
  )}
</CounterToggle>

Mouse/Scroll components.

Already started in prototype. I've plan to add these both 'trackers'

Tests

added some simple tests in 0.1.x

Yes, add tests. Tests are important.

TypeScript / Flow Definitions

added flow types by @TrySound in 0.1.x

Add typings.

Preact Interop

I need to study how can I make this interop, but it's on my mind. Not a hight priority.

Website & Docs

Gatsby? Maybe. Need to think about.

Object.assign breaks IE 11

image

IE11 seems to be blowing up from the use of Object.assign. I'm working on a PR to write the exports out explicitly for now. We could do something like this, but I think since these aren't written very much it shouldn't be too hard to update manually.

Mobile support on *Active* and *Hover* component

For now, the Active component simulates active state on any HTML element by using onMouseUp and onMouseDown and Hover component uses omMouseEnter and onMouseLeave events.

The issue is on mobile devices. Mobile devices can control and simulate those behaviors by using events such ontouchstart and ontouchend. Right now, these components don't work on mobile devices.

I'd like to contribute and add the behavior if you're looking into supporting mobile devices too. will be happy to send a PR for it.

Compose refs helper

I was curious if you had any ideas about some type of utility to help with composing multiple ref functions that could possibly be added to this library? I'm thinking it would be similar to the composeEvents function we talked about in #32.

I'm wondering if there is any way to memoize all the function calls so we can avoid this caveat when composing multiple refs? It would be really cool if this could be baked into composeEvents as well.

This is my idea:

function memoize(fn) {
  memoize.cache = {}
  return function() {
    var key = JSON.stringify(arguments)
    if (memoize.cache[key]) {
      return memoize.cache[key]
    } else {
      var val = fn.apply(this, arguments)
      memoize.cache[key] = val
      return val
    }
  }
}

function memoizeRefs(...fns) {
  return node => {
    fns.forEach(fn => memoize(fn))
  }
}

What is the point is "Loading" component if we already have "Toggle"?

Toggle

const Toggle = ({ initial = false, ...props }) => (
  <State initial={{ on: initial }}>
    {({ state, setState }) => renderProps(props, {
      on: state.on,
      off: !state.on,
      toggle: () => setState(s => ({ on: !s.on })),
      setOn: (on) => setState({ on }),
    })}
  </State>
)

Loading

const Loading = ({ initial = false, ...props }) => (
  <State initial={{ loading: initial }}>
    {({ state, setState }) => renderProps(props, {
      isLoading: state.loading,
      toggleLoading: () => setState(s => ({ loading: !s.loading })),
      setLoading: (loading) => setState({ loading }),
    })}
  </State>
)

The logic is the same except names

make typings of children/render prop optional to allow composition

Right now you either have to have a render or children prop

export type ValueProps<T> =
  | { initial: T; onChange?: ValueChange<T>; render: ValueRender<T> }
  | { initial: T; onChange?: ValueChange<T>; children: ValueRender<T> }

So you can't do this:

const Container = adopt({
  state: <State initial={{ foo: '' }} />,
})

rename inc -> increment, dec -> decrement

Since we're breaking API's before 1.0 ๐Ÿ˜‡ how would you feel about renaming inc to increment and dec to decrement since the pattern of the other components all use full names? For example, value instead of val. I can submit a PR for this if you are on board, please let me know.

What kind of components should this lib have?

Have you guys draw a line on what kind of components this lib should have? I have plenty of possible things that could come here and are already developed, but I'm unsure they fit this lib's purpose. Some:

Promised

<Promised promise={ Promise.resolve('value') }>
  { (value, loading) => (
    loading ? 'loading...' : <div>Result: { value }</div>
  ) }
</Promised>

MatchMedia

<MatchMedia breakpoints={ { small: '(max-width: 400px)', 'big': '(min-width: 401px)' } }>
  { match => (
    <div>
      I'm on { match === 'small' ? 'mobile' : 'desktop' }
    </div>
  ) }
</MatchMedia>

Proposal to standardize State Container components

I noticed that many of them have a very similar behavior, and I end up having to repeat a lot of code for each one of them. I am always apprehensive when it comes to abstractions, but I believe that in many cases it is extremely useful. My idea is to create a helper to create these State Container components.

E.g.:

const Toggle = createStateContainer({
  displayName: 'Toggle',
  initialState: false,
  renderProps: ({ state, setState }) => ({
    on: state,
    toggle: () => setState(state => !state)
  })
})
const complement = fn => (...args) => !fn(...args)

const List = createStateContainer({
  displayName: 'List',
  initialState: [],
  renderProps: ({ state, setState }) => ({
    list: state,
    first: () => state[0],
    last: () => state[Math.max(0, state.length - 1)],
    push: (value) => setState(s => ([ ...s, value ])),
    reject: (fn) => setState(s => s.filter(complement(fn))),
    filter: (fn) => setState(s => s.filter(fn)),
    map: (fn) => setState(s => s.map(fn)),
  })
})

Even within more complex cases:

const hasItem = arr => item => arr.indexOf(item) !== -1
const removeItem = item => arr => hasItem(arr)(item) ? arr.filter(d => d !== item) : arr
const addUnique = item => arr => hasItem(arr)(item) ? arr : [...arr, item]

const Set = createStateContainer({
  displayName: 'Set',
  initialState: [],
  renderProps: ({ state, setState }) => ({
    values: state,
    add: item => setState(addUnique(item)),
    remove: item => setState(removeItem(item)),
    has: hasItem(state),
  })
})
// This Mouse component does not yet exist in powerplug but it is a very old wish
// I've used as complex example.
// This is a raw implementation: https://codesandbox.io/s/vnx11ymv67 (with some differences)

const Mouse = createStateContainer({
  displayName: 'Mouse',
  initialState: {
    x: null,
    y: null,
    ratioX: null,
    ratioY: null,
    pageX: null,
    pageY: null
  },
  renderProps: ({ setState }, { ratioFix /* props passed to <Mouse> */}) => ({
    bind: {
      onMouseEnter: (event) => {
        // cache for better performance
        this.boundingClientRect = event.currentTarget.getBoundingClientRect() 
      },
      onMouseMove: (event) => {
        const { left, top, width, height } = this.boundingClientRect
        const { clientX, clientY, pageX, pageY } = event

        const x = (clientX - left + 1).toFixed(3)
        const y = (clientY - top + 1).toFixed(3)

        const useRatioFix = typeof ratioFix !== 'undefined' ? ratioFix : 3
        const ratioX = Math.min(1, x / width).toFixed(useRatioFix)
        const ratioY = Math.min(1, y / height).toFixed(useRatioFix)

        setState({ x, y, pageX, pageY, ratioX, ratioY })
      }
    }
  })
})

In this implementation, I overwritten the setState to accept all types (boolean, string, number, array, object, etc), so we can simplify it even more.

Things to note:

  • All components will have state and setState for its primary purpose. For example, state for Toggle will be the boolean value of "on" or "off". In this case we can use the Toggle method toggle or if need, setState directly. Same for all other components.
<Toggle initial={false}>
 {({ state, setState, on, toggle }) => ()}
</Toggle>

<Mouse>
 {({ state, setState, bind }) => <div {...bind}>{state.ratioX}</div>} 
</Mouse>

// etc

I think we can gain some benefits like:

  • Standardize all the components. So all will have the same behavior. (I could easily type some components with a 'StateContainer' generic type in TypeScript)
  • No more code repetition, like onChange binding, renderProps util, etc.
  • We can export this createStateContainer thing, so users can create their own state containers.

And finally, here is a draft of the implementation:

import * as React from 'react'
import renderPropsUtil from './renderProps'

const isFn = arg => typeof arg === 'function'
const cbs = (...fns) => (...args) => fns.forEach(fn => isFn(fn) && fn(...args))

export const createStateContainer = ({
  initialState,
  renderProps,
  displayName,
}) => {
  return class StateContainer extends React.Component {
    static displayName = displayName || 'StateContainer'

    // We use `value` as main state for all components
    // so we can save simple values that are not objects
    state = {
      value:
        typeof this.props.initial === 'undefined'
          ? initialState
          : this.props.initial,
    }

    setStateValue = (setter, cb) =>
      this.setState(
        ({ value }) => ({ value: isFn(setter) ? setter(value) : setter }),
        cbs(cb, this.props.onChange)
      )

    stateHandler = () => ({
      state: this.state.value,
      setState: this.setStateValue,
    })

    // Bind this to renderProps fn so we can access
    // this while creating our custom methods/properties
    renderProps = renderProps.bind(this)
    propsToPass = () => this.renderProps(this.stateHandler(), this.props)

    render() {
      return renderPropsUtil(this.props, {
        ...this.stateHandler(),
        ...this.propsToPass(),
      })
    }
  }
}

I'm still working locally on this idea, but before proceeding I'd like to hear from you.

Compose using component names? What about uglify?

Am I missing something? I feel like when components are uglified, their names will be changed and my compose will be broken... Shouldn't it instead be array position? Or I should name my components explicitely?

Either:

const ToggleCounter = compose(Toggle, Counter)

<ToggleCounter>{([toggle, counter]) => <div />}</ToggleCounter>

Or

const ToggleCounter = compose({toggle: Toggle, counter: Counter})

<ToggleCounter>{({toggle, counter}) => <div />}</ToggleCounter>

Otherwise uglify is going to break this.

Compose events helper

Would you consider adding a helper utility to help compose events if you want to use the same ones being used by a react-powerplug component?

A contrived example, but I'm using something like this right now:

function composeEvents(internalEvents, externalEvents) {
  return Object.keys(externalEvents).reduce((composedEvents, key) => {
    const internal = internalEvents[key]
    const external = externalEvents[key]
    return {
      ...composedEvents,
      [key]: internal
        ? (event, ...args) => {
            internal(event, ...args)
            external(event, ...args)
          }
        : external,
    }
  }, {})
}

// usage
const HoveredDiv = ({ children, onMouseEnter, onMouseLeave, ...restProps }) => (
  <Hover>
    {({ isHover, bindHover }) => (
      <div
        {...composeEvents({ onMouseEnter, onMouseLeave }, bindHover)}
        {...restProps}
        children={children(isHover)}
      />
    )}
  </Hover>
)

Why are there no control props for the state management components?

I was wondering why components like State, Toggle and Value accept an initial value but don't accept a controling value?

Something like this:

<State state={myState}>
  ...
</State>

Why do you need that? I used State to represent the state of a form, which was initially populated by an object. Next the differrent form components would modify the local State. And in the end that State would be submitted to the remote API. This resulted in an updated object, which then populated the form State again. Because State does not accept changed props I had to write my own component.

My guess was you wanted to skip having to compare states of State to make it work. Or is there more to it?

Refactor components' `onChange` to match same `initial` type

I find it very inconsistent that you pass initial={boolean} to <Toggle> and onChange fires with {on: boolean}.
I think onChange should call with the fundamental component's state

Toggle -> boolean (on)
Counter -> number (count)
Value -> any (value)
List -> array (list)
Hover -> boolean (isHovered)

Example:

<Counter onChange={count => console.log(count)}>
 {/* ... */}
</Counter>

etc...

ShouldUpdate

Do you think it would be beneficial to have a ShouldUpdate component? ๐Ÿค”

I was reading through these docs and it seemed like it could be nice to have.

Not sure how this would even work. But could maybe look something like:

<ShouldUpdate
  when={(currentProps, nextProps) =>
    currentProps.students !== nextProps.students
  }
>
  // only update sometimes
</ShouldUpdate>

Might be better to just make another component honestly ๐Ÿ˜… curious to hear your thoughts.

Integrate another CI

I was using buddy, but it have some limitations. I need to find & integrate another CI (circle, travis, etc.) that covers our needs

Will be nice have example how to listen changes for parents

So now I can see it like this for example:

class Checkbox extends PureComponent {
  static propTypes = {
     onOutsideChange: PropTypes.func.isRequired,
  }

  render() {
    const { onOutsideChange, checked } = this.props;
    return (
      <Toggle initial={ checked }>
        {({ on, toggle }) => (
          <input
            type='checkbox'
            checked={ on }
            onChange={ () => { toggle(), onOutsideChange(!on) } }
          />
        )}
      </Toggle>
    );
  }
}

Can we do It in easier way? For example:

    const { onOutsideChange, checked } = this.props;
    return (
      <Toggle initial={ checked } onToggle={ onOutsideChange }>
        {({ on, toggle }) => (
          <input
            type='checkbox'
            checked={ on }
            onChange={ toggle }
          />
        )}
      </Toggle>

add `reset/resetState`?

Hey, thanks for this great library!

I find myself setting initial= values, updating them over time, and then performing an action at a certain point. And then, often after performing the action I need to "reset" the values to their initial state again, to build towards another action.

It would be nice if in addition set(value), the components were passed a reset() function as well that always reset the value to the initial value.


(Side note, why is <State> the only component that takes the function named setState instead of simply set to stay consistent with the others?)

FocusManager fails with buttons in safari and firefox

We need to find some solution to work around it both safari and firefox

Ref https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#Clicking_and_focus

Example

<html>

<body>
<button tabindex="-1">Click me</button>

<script>

const button = document.querySelector("button")

// focus event is fixed with custom firing
button.addEventListener("mousedown", event => {
  if (event.currentTarget.tagName === 'BUTTON') {
    event.currentTarget.focus();
  }
});

button.addEventListener("focus", () => {
  console.log("focus");
});

// sadly blur is fired straight after focus
button.addEventListener("blur", () => {
  console.log('blur');
})

</script>
</body>

</html>

/cc @souporserious

Composing two of the same kind

I realized this when trying to replace my own render props composer with the one in this library.

Imagine the following scenario:

<Toggle initial={true}>
  {outer => (
    <Toggle initial={false}>
      {inner => (
        <Checkbox checked={outer.on} onChange={outer.toggle} />
        <Checkbox checked={inner.on} onChange={inner.toggle} />
      )}
    </Toggle>
  )}
</Toggle>

Given that Compose uses the name of the component to build the props, I'm not sure if this is possible to compose.

<Compose
  states={[
    <Toggle initial={true} />,
    <Toggle initial={false} />,
  ]}
>
  // The line below shows the problem, we have a name clash in the prop names
  {({ toggle, toggle }) => (
    <Checkbox checked={toggle.on} onChange={toggle.toggle} />
    <Checkbox checked={toggle.on} onChange={toggle.toggle} />
  )}
</Compose>

If Composed passed the different props of the composed components as positional arguments to the unified render prop function, this would be solved:

<Compose
  states={[
    <Toggle initial={true} />,
    <Toggle initial={false} />,
  ]}
>
  {(outer, inner) => (
    <Checkbox checked={outer.on} onChange={outer.toggle} />
    <Checkbox checked={inner.on} onChange={inner.toggle} />
  )}
</Compose>

If you think about it, it makes more sense, if you provide a sequence of components to be composed, that you get the corresponding sequence of props that each of them provide, instead of an object of props.

If you agree I'm more than happy to make a PR myself.

implement defense of dot js-style poly package structure?

Hi guys!
I recently learned a bunch of stuff about how to reduce webpack bundle size while supporting SSR, and I've been making PRs to some of my favorite packages to support this (just got one merged in redux-form: redux-form/redux-form#4123)

Would you guys be open to a PR for the same here? I admit I don't know as much about the relative advantages of the rollup build you guys are using, but I believe that the approach outlined below would enable people to save more KB in their Webpack bundle.

Basically the package structure is like this, as recommended by the In Defense of .js proposal:

package.json

{
  "main": "./index.js",
  "modules.root": "./es",
  "sideEffects": false
}

Package structure

  • node_modules/react-powerplug/
    • package.json
    • index.js <-- transpiled code goes in the project root
    • State.js
    • etc.
    • es/ <-- untranspiled ES2015 goes in here (directory name can be whatever, just has to match modules.root)
      • index.js
      • State.js
      • etc.

With this setup, import State from 'react-powerplug/State' works equally well in node and webpack, resolving to:

Environment Path
Node node_modules/react-powerplug/State.js
Webpack node_modules/react-powerplug/es/State.js

Additionally, even with Webpack tree shaking, there is less overhead with import State from 'react-powerplug/State.js' than with import {State} from 'react-powerplug'.

Thoughts?

I also know this library is intended more for prototyping than production use, but I'm realizing that <Toggle> would be the most convenient way to handle the various cases of user-toggled collapsible panes in my application.

TypeScript definitions

I know that supporting all type systems is a hard work, but without them life is not so sweet.

I am happy to make a PR, but you have to decide:

  • you are happy to ship d.ts along library, so lets add them here.
  • types is the right place for types, so please definitely publish them.

I'll prefer the first way, but it is up to you.

<Debounce /> and <Throttle /> component

Everytime that we need to make a debounce or a throttle on some method the process is very annoying. I think that maybe this two components can be useful:

Debounce

<Debounce method={() => console.log('helo')} timer={200}>
  {({ fn }) => /* ... */}
</Debounce>

Throttle

<Throttle method={() => console.log('helo')} timer={200}>
  {({ fn }) => /* ... */}
</Throttle>

add Ref with react ref api

<Ref>
  {ref => (
    <React.Fragment>
      <button onClick={e => ref.current.openModal()}>open</button>
      <Modal ref={ref}>
        <div>model_content</div>
        <button onClick={e => ref.current.closeModal()}>close</button>
      </Modal>
    </React.Fragment>
  )}
</Ref>

react-powerplug breaking build after babel 7 upgrade

Hi!

Using version 1.0.0-rc.1 breaks the build when upgrading to babel 7.

Module not found: Error: Can't resolve '@babel/runtime/helpers/builtin/es6/objectWithoutProperties' in '.../node_modules/react-powerplug/dist'

Any clue what is happening?

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.