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

react-mirror-old-compatibility

v0.3.5

Published

Fractal state management for react

Downloads

7

Readme

React Mirror

This guide assumes deep familarity with Redux

A fractal state tree that wraps your views.

  • Atomicity - all state lives in one place only
  • Co-location - views don't rely on external modules

Quick demo:

import React from 'react'
import Mirror from 'react-mirror'

const Counter = Mirror({
  reducer: ({value = 0}, {type, payload = 1}) => {
    switch (type) {
      case 'INCREMENT': return {value: value + payload}
      case 'DECREMENT': return {value: value - payload}
    }
  }
})(
  ({value, dispatch}) => (
    <div>
      Value: {value}
      <button onClick={() => dispatch('INCREMENT')}>+</button>
      <button onClick={() => dispatch('DECREMENT')}>-</button>
    </div>
  )
)

What is React Mirror

React Mirror wraps components with redux-like stores. Stores are automatically composed into a single state tree that mirrors your view tree. You can pass state up (via subscribe) & down (via props). You can also pass state down multiple levels at once with React Mirror's powerful context feature. Parents cannot freely change the props of a component decorated by React Mirror, instead appropiate actions are dispatched to the child's reducer.

You've probably heard context is fundamentally broken in React, that's true. React Mirror's implementation avoids React's pitfalls & works reliably.

Why use React Mirror

Some popular state-management solutions put everything in a single global store. This improves debugging, introspection, convenience & enables some features like time-travel. Downsides include tight coupling between seperate modules & complex architecture that's tedious to write code for (eg, action creators, thunks).

By realizing local stores can be composed just like views & allowing context you can mitigate those disadvantages whilst keeping the perks of single-store solutions. Local state is ideal for reusing components & fast feature iteration, whilst context is an essential convenience for behaviour that depends on several views.

Usage

Mirror(config, options)

Creates a decorator you can pass a component to. The decorated component's props are controlled by the store which can be updated indirectly via actions.

config

reducer(currentState, {type, payload, ...}, props, context) (Function):

Returns the next state, given the current state, an action, parent props & contextual state. context's object properties include the state of ancestors picked via contextSubscribe.

enhancer() (Function):

Top-level only. You can use most Redux store enhancers to add third-party capabilities to React Mirror.

contextSubscribe (String | String[]):

Allows child components to access ancestor state (via props & reducer). Child components can also dispatch actions to thier ancestors.

contextPublish (String):

Allows all descendants to access a component's state & dispatch actions to the component.

const Ancestor = Mirror({
  reducer: (currentState, {type}) => {
    switch (type) {
      case 'ACTION_DISPATCHED_BY_DESCENDANT': return /* ... */
      /* ... */
    }
  },
  contextPublish: 'ancestor'
})(
  () => { /* ... */ }
)

const Descendant = Mirror({
  reducer: (currentState, action, props, {ancestor}) => { /* ... */ },
  contextSubscribe: 'ancestor'
})(
  ({dispatch, ...state}) => (
    <div>
      { /* ... */ }
      <button
        onClick={() => dispatch('ACTION_DISPATCHED_BY_DESCENDANT', 'ancestor', null)}
      >Click me!</button>
    </div>
  )
)
options

pure (Function):

Mirror will avoid re-renders if, after dispatching an action, pure returns false. Default value: shallowEqual.

Props

You can pass subscribe to decorated components, this might be useful for reacting to input changes within a form. The decorator passes the reducer state & some props to the wrapped component: subscribe, dispatch & context.

subscribe(action, state, prevState) (Function):

Called immediately after reducer handles action & before component renders. Useful for running side-effects in response to actions. Subscriptions are automatically cancelled when the component unmounts, but you can unsubscribe earlier by calling the function returned by subscribe. I suggest creating subscriptions inside componentWillMount.

const Input = Mirror({ /* ... */ })(() => { /* ... */ })

const Form = () => (
  <form>
    <Input subscribe((action, {value}) => console.log(`New input value: ${value}`)) />
  </form>
)
const MyComponent = Mirror({ /* ... */ })(
  class MyComponent extends Component {
    componentWillMount() {
      this.unsubscribe = this.props.subscribe((action, state, prevState) => { /* ... */ })
    }
    render() { /* ... */ },
  }
)

dispatch(type, [context], [payload], [metadata]) | ({type, payload, ...metadata}, [context]) (Function):

Calls the reducer with an action. If context is undefined the action is dispatched to the local store.

Actions

INITIALIZE():

Called before component mounts.

UPDATE_PROPS():

Called when parent updates child props. Parents cannot freely update wrapped child props, you'll need to return the updated state from the reducer for prop changes to have any effect (props are the third argument).

UPDATE_CONTEXT(updatedContextName):

Called immediately after parent reducer handles action & before rendering.

UNMOUNT_COMPONENT():

Called before child unmounts.

Caveats

React Mirror isn't complete yet. & some of this functionality may be essential for your use case. For example:

  • Store IDs are non-deterministic. This makes state rehydration much harder, & snapshot testing slightly harder.
  • No API exists for accessing the wrapped component.
  • Root store isn't destroyed when top-level component unmounts.
  • Ancestors cannot dispatch actions to children. Not architectually sound, but escape hatch should be available.
  • Cannot add middleware to child stores.
  • Cannot access instance inside pure / reducer.
  • No interface exists for selectors

Additionally:

  • Parents cannot directly update child props.
  • Root state changes as stores are added / removed & may be tricky to debug.

If you have a potential solution to any of these open an issue & we can discuss it.

Thanks

React Mirror was inspired by Cycle.js onionify, Redux & the Controller View pattern.

Share React Mirror on Twitter if you like it