Skip to content
This repository has been archived by the owner on Nov 14, 2022. It is now read-only.

vikingair/react-rewired

Repository files navigation

GitHub license npm package Travis Coverage Status styled with prettier

react-rewired

Wire your react app as easy as possible...

  • A fast and easy alternative for react-redux
  • Feels more like react (using internal react state as base model)
  • TypeScript support included
  • flow support included
  • very small package size (v3.0.2 -> 836 B gzipped)
  • high performance (performance play ground in comparison to react-redux@7.1.1 is available here)

Introduction

react-rewired intends to reduce the overhead and complexity when internal react component state is not enough, because you want to store everywhere accessible data which can safely be accessed by your react components and triggers automatically updates, when the data changes.

This should be familiar to any react developer. And the more popular solution for this purpose is the usage of react-redux. But react-redux comes not only with nice dev tools, good documentation, large user base and a consistent philosophy for its usage and idea. In my opinion, I am still looking to often into the dev tools to find out what seems to be broken and my application grows very fast too large with all the overhead, that comes with actions and reducers. Also it is always quite an act to implement flow on top of react-redux.

How to - an example

First of all you need to initialize your Store with some defaults.

import { Wired } from 'react-rewired';

const Store = Wired.store({
    num: 12,
    data: { keys: [] },
    foo: null
})

You will also have to define the types for your State because the breaking change since flow reached version 0.85 requires to define any types in value position. Now let's e.g. define the array on data.keys to contain only strings.

type DataState = { keys: string[] };
type State = {
    num: number,
    data: DataState,
    foo: string | null
};

const data: DataState = { keys: [] }; // you can directly type cast
const Store = Wired.store<State>({
    num: 12,
    data,
    foo: null
})
  • HINT: Types in value position are still very new, but every flow-parser is able to handle those. If you run in troubles with those, you should maybe consider to update your dev dependencies.

The next step is the wrapping of your react tree with the context provider to be able to access the data within any component of the subtree.

const root = document.getElementById('root');
root &&
    ReactDOM.render(
        <Store.root>
            <App />
        </Store.root>,
        root
    );

To access the data within your component you want to wrap it into the corresponding consumer. (HINT: You should not define react components inline but give it a name, because the react dev tools uses that name as default display name. E.g. in the following snippet "MyComponent" will be displayed inside the react dev tools)

type MyComponentStoreProps = {| key?: string, odd: boolean |};
type MyComponentOwnProps = {||};
type MyComponentProps = {| ...MyComponentOwnProps, ...MyComponentStoreProps |};

const MyComponent = ({ key, odd }: MyComponentProps) => <JSX />
// and not wire it
const MyWiredComponent = Store.wire<MyComponentStoreProps, MyComponentOwnProps>(
    MyComponent,
    state => ({
        key: state.data.keys[0],
        odd: state.num % 2 === 1
    })
);
  • HINT: I want to stress out that your application will benefit a lot if you follow the above pattern everywhere. Also I want to strongly encourage you, to use always (if possible) exact prop types, to ensure that no arbitrary props will be provided where not expected.

The last thing you have to know are state updates. There are two different ways you can perform those updates. Just like setState on internal react state.

// simply return those keys you want to update with the new data
// they will be merged and component updates will only be propagated
// to those components who listen to those data points
Store.set({ num: 13, foo: 'bar' });
// MyWiredComponent would update after this change
// because the value of "odd" changed from "false" to "true"

// return a function which will be called with the current state
Store.set(state => ({ num: 3 * state.num })); // num = 3 * 13 = 39
// MyWiredComponent would NOT update after this change
// because the values of "odd" and "key" did not change

If you want to access the data directly e.g. outside any component, you can do the following:

const state = Store.get();
  • ATTENTION: Modifications to the returned state can effect your application in an unexpected way. Even if you know what you're doing, I do not recommend it.

  • To make testing easy even with the context API, you should insert a little snippet into your setupTests.js (see jest option "setupFilesAfterEnv")

Store.set = (Component, mapStateToProps) => {
    const result = props => <Component {...mapStateToProps(Store.get())} {...props} />;
    result.displayName = `Wired(${Component.displayName || Component.name || ''})`;
    return result;
}