React, but with built-in global state management.

Overview

ReactN Tweet version minzipped size downloads build chat

ReactN is an extension of React that includes global state management. It treats global state as if it were built into React itself -- without the boilerplate of third party libraries.

banner

For support, reach out to us on the Reactiflux Discord channel #reactn.

💗 this project? Become a sponsor.

Install

  • npm install reactn or
  • yarn add reactn

Features

No boilerplate

For function components, import { useGlobal } from 'reactn'; to harness the power of React Hooks!

For class components, simply change import React from 'react'; to import React from 'reactn';, and your React class components will have global state built in!

If you prefer class decorators, you can continue to import React from 'react'; for your components and additionally import reactn from 'reactn'; for access to the @reactn decorator!

Intuitive

Function components

Global state in function components behaves almost identically to local state.

You use [ global, setGlobal ] = useGlobal() to access the entire global state object.

You use [ value, setValue ] = useGlobal(property) where property is the property of the global state you want to get and set.

Global reducers in function components behaves almost identically to local reducers.

You use dispatch = useDispatch(reducerFunction) to mimic the behavior of useReducer, where instead of providing an initial state, the state of the reducer is the ReactN global state object.

You use dispatch = useDispatch(reducerName) to use a reducer that was added by the addReducer helper function.

You use dispatch = useDispatch(reducerFunction, property) or [ value, dispatch ] = useDispatch(reducerFunction, property) to apply a reducer specifically to a global state property. This is very similar to React's native useReducer functionality.

Class components

Global state in class components behaves exactly like local state!

You use this.global and this.setGlobal to get and set the global state.

You use this.dispatch.reducerName() to dispatch to a reducer that was added by the addReducer helper function.

The @reactn decorator allows you to convert classes that extend React.Component to ReactN global state components.

Map state to props

If you prefer Redux's connect functionality, pure functions, or are dealing with deeply nested objects, a withGlobal higher-order component is also available.

Table of contents

Getting started

Managing multiple states

This README is for managing a single global state. This is ideal for most applications. If you are using concurrent server-side rendering or otherwise want to work with multiple global states, follow the README for the Provider component, which allows you to limit a ReactN state to a React Context.

If you are unsure whether or not you need multiple global states, then you do not need multiple global states.

Initializing your state

You can initialize your global state using the setGlobal helper function. In most cases, you do not want to initialize your global state in a component lifecycle method, as the global state should exist before your components attempt to render.

It is recommended that you initialize the global state just prior to mounting with ReactDOM.

import React, { setGlobal } from 'reactn';
import ReactDOM from 'react-dom';
import App from './App';

// Set an initial global state directly:
setGlobal({
  cards: [],
  disabled: false,
  initial: 'values',
  x: 1,
});

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

TypeScript support

ReactN supports TypeScript out of the box! It is written entirely in TypeScript. This gives it powerful intellisense, auto-complete, and error-catching abilities.

TypeScript can maintain inferred global state and reducer shape of a Providers. Unfortunately, without your help, it cannot track the shape of the "default" global state -- the one manipulated by the setGlobal and addReducer helper functions.

In order to tell TypeScript the shape of your global state when you are not using a Provider, create a file at src/global.d.ts with the following contents:

import 'reactn';

declare module 'reactn/default' {

  export interface Reducers {

    append: (
      global: State,
      dispatch: Dispatch,
      ...strings: any[]
    ) => Pick<State, 'value'>;

    increment: (
      global: State,
      dispatch: Dispatch,
      i: number,
    ) => Pick<State, 'count'>;

    doNothing: (
      global: State,
      dispatch: Dispatch,
    ) => null;
  }

  export interface State {
    count: number;
    value: string;
  }
}

In the above file, we extend the Reducers and State interfaces in the 'reactn/default' file. While you will never use 'reactn/default' in your code, ReactN will use it to determine the shape of the default global state.

The above example will add append, increment, and doNothing to your useDispatch and this.dispatch auto-completion and typing. The parameters and return values will also be correctly typed. In addition, it will also add count and value to your useGlobal and this.global auto-competion with the appropriate types as well.

Developer tools

ReactN is compatible with the Redux DevTools extension.

  • Install the Redux DevTools extension to your browser or environment.
  • Install the redux package to your project via npm or yarn. This is used to create a middleware Redux store for the Redux DevTools extension.
    • You do not have to import or use the redux package anywhere in your project.
    • You do not need to create a Redux store, reducer, or actions.
    • redux is just a peer dependency. It will be managed automatically.
  • Follow the instructions on the ReactN DevTools README.

Examples

Class components

By importing React from reactn instead of react, you bake global state directly into the React namespace. As a result, Component and PureComponent will have access to the global and dispatch member variables and setGlobal method.

import React from 'reactn'; // <-- reactn
import Card from '../card/card';

// Render all cards in the global state.
export default class Cards extends React.PureComponent {
  componentDidMount() {
    // Hydrate the global state with the response from /api/cards.
    this.setGlobal(
      // Despite fetch returning a Promise, ReactN can handle it.
      fetch('/api/cards')
        .then(response => response.json())

        // Set the global `cards` property to the response.
        .then(cards => ({ cards }))

        // Fail gracefully, set the global `error`
        //   property to the caught error.
        .catch(err => ({ error: err })),
    );
  }

  render() {
    // For each card in the global state, render a Card component.
    // this.global returns the global state,
    //   much the same way this.state returns the local state.
    return (
      <div>
        {this.global.cards.map(card => (
          <Card key={card.id} {...card} />
        ))}
      </div>
    );
  }
}

Class components (with decorator)

By importing React and ReactN separately, the React namespace remains unchanged. You can inject ReactN's global functionality into your vanilla React component by using the @reactn decorator imported from the reactn package.

import React from 'react';
import reactn from 'reactn'; // <-- reactn
import Card from '../card/card';

// Render all cards in the global state.
@reactn
export default class Cards extends React.PureComponent {
  componentDidMount() {
    // Hydrate the global state with the response from /api/cards.
    this.setGlobal(
      // Despite fetch returning a Promise, ReactN can handle it.
      fetch('/api/cards')
        .then(response => response.json())

        // Set the global `cards` property to the response.
        .then(cards => ({ cards }))

        // Fail gracefully, set the global `error`
        //   property to the caught error.
        .catch(err => ({ error: err })),
    );
  }

  render() {
    // For each card in the global state, render a Card component.
    // this.global returns the global state,
    //   much the same way this.state returns the local state.
    return (
      <div>
        {this.global.cards.map(card => (
          <Card key={card.id} {...card} />
        ))}
      </div>
    );
  }
}

Function components

Using React Hooks, you can harness useGlobal to access the global state.

import React, { useGlobal } from 'reactn'; // <-- reactn
import Card from '../card/card';

// Render all cards in the global state.
const Cards = () => {
  // Use the hook to get all cards in the global state.
  //   setCards is not used in this example.
  const [cards, setCards] = useGlobal('cards');

  // For each card in the global state, render a Card component.
  return (
    <div>
      {cards.map(card => (
        <Card key={card.id} {...card} />
      ))}
    </div>
  );
};

export default Cards;

You may also use the useDispatch hook analogously to the useReducer hook by providing a function to useDispatch.

import React, { useDispatch } from 'reactn'; // <-- reactn

const incrementReducer = (global, dispatch, action) => ({
  count: global.count + action.amount,
});

const decrementReducer = (global, dispatch, action) => ({
  count: global.count - action.amount,
});

const MyComponent = () => {
  const increment = useDispatch(incrementReducer);
  const decrement = useDispatch(decrementReducer);

  return (
    <div>
      <button onClick={() => increment({ amount: 1 })}>Add 1</button>
      <button onClick={() => increment({ amount: 3 })}>Add 3</button>
      <button onClick={() => decrement({ amount: 5 })}>Subtract 5</button>
    </div>
  );
};

export default MyComponent;

By providing a second parameter to useDispatch that is the key of the global state, the return value of that reducer will set that property of the global state. This allows you to write your reducers similar to React's useReducer.

import React, { useDispatch } from 'reactn'; // <-- reactn

const incrementReducer = (count, action) => count + action.amount;

const decrementReducer = (count, action) => count - action.amount;

const MyComponent = () => {
  const increment = useDispatch(incrementReducer, 'count');
  const decrement = useDispatch(decrementReducer, 'count');

  return (
    <div>
      <button onClick={() => increment({ amount: 1 })}>Add 1</button>
      <button onClick={() => increment({ amount: 3 })}>Add 3</button>
      <button onClick={() => decrement({ amount: 5 })}>Subtract 5</button>
    </div>
  );
};

export default MyComponent;

Helper functions

addCallback

Use addCallback to execute a function whenever the state changes. The return value of the callback will update the global state, so be sure to only return undefined or null if you do not want the global state to change. Be aware that always returning a new state value will result in an infinite loop, as the new global state will trigger the very same callback.

The only parameter is the callback function.

import { addCallback, setGlobal } from 'reactn';

// Every time the global state changes, this function will execute.
addCallback(global => {
  alert(`The new value is ${global.value}!`);

  // If the global state was changed to 1, change it to 2.
  if (global.value === 1) {
    return { value: 2 };
  }

  // If the global state is anything other than 1, don't change it.
  return null;
});

setGlobal({ value: 1 });
// The new value is 1!
// The new value is 2!

The return value of addCallback is a function that, when executed, removes the callback.

import { addCallback, setGlobal } from 'reactn';

const removeAlert = addCallback(global => {
  alert(global.value);
});

// The callback causes an alert on global state change:
setGlobal({ value: 1 }); // 1
setGlobal({ value: 2 }); // 2

// No longer execute the callback.
removeAlert();

// No alerts:
setGlobal({ value: 3 });
setGlobal({ value: 4 });
addReducer

Use addReducer to add a reducer to your global state.

The first parameter is the name of your reducer. You will access your reducer by this name. this.dispatch.reducerName or useDispatch('reducerName').

The second parameter is the reducer function. The reducer function that you write has at least two parameters: first, the global state; second, a map of your reducers. The third and onward parameters are the arguments that you pass when dispatching. The reducer function that you use when dispatching does not contain the global state or map of reducers. Those are prefixed for you automatically.

import { addReducer, setGlobal, useDispatch, useGlobal } from 'reactn';

// Initialize the global state with the value 0.
setGlobal({ value: 0 });

// When the increment reducer is called, increment the global value by X.
addReducer('increment', (global, dispatch, x = 1) => ({
  value: global.value + x,
}));

function MyComponent() {
  const increment = useDispatch('increment');
  const [value] = useGlobal('value');
  return (
    <>
      The value is{' '}
      <button
        onClick={() => {
          // Increment from 0 to 1.
          // (the default value of the reducer is 1)
          if (value === 0) {
            increment();
          }

          // Increment from 1 to 5.
          else if (value === 1) {
            increment(4);
          }
        }}
        value={value}
      />
    </>
  );
}

For a class component, the analogous method is this.dispatch.increment(value).

The dispatch parameter on a reducer allows you to write "sagas," or a single reducer that dispatches other reducers.

// add(1)
addReducer('add', (global, dispatch, i) => ({
  x: global.x + i,
}));

// subtract(2)
addReducer('subtract', (global, dispatch, i) => ({
  x: global.x - i,
}));

// addSubtract(1, 2)
addReducer('addSubtract', async (global, dispatch, i, j) => {
  await dispatch.add(i);
  await dispatch.subtract(j);
});
addReducers

addReducers accepts an object where the keys are reducer names and the values are reducers. addReducers is just a convenient shorthand for calling addReducer multiple times.

getDispatch

Use getDispatch to return an object of the global dispatch functions. You only want to use this in helper libraries, and not in Components. Components should use useDispatch or this.dispatch.

getDispatch has no parameters.

import { getDispatch } from 'reactn';

// Access this.dispatch.reducerName outside of a Component.
class HelperLibrary {
  getDispatcherFunction() {
    return getDispatch().reducerName;
  }
}
getGlobal

Use getGlobal to return a current snapshot of the global state. You only want to use this in helper libraries, and not in Components. Components should use useGlobal or this.global to ensure that they re-render when the global state changes. getGlobal will not cause a Component reliant on the global state to re-render, nor will it cause a library function to re-execute. It does nothing more than return a current snapshot of the global state.

getGlobal has no parameters.

import { getGlobal } from 'reactn';

// Access this.global.value outside of a Component.
class HelperLibrary {
  getGlobalValue() {
    return getGlobal().value;
  }
}
removeCallback

Use removeCallback to remove a callback that was added via addCallback. The callback must be the same function reference. This is equivalent to executing the return value of addCallback.

The only parameter is the callback function itself.

import { addCallback, removeCallback, setGlobal } from 'reactn';

function alertCallback(global) {
  alert(global.value);
}

addCallback(alertCallback);

// Alerts the global state value:
setGlobal({ value: 1 }); // 1
setGlobal({ value: 2 }); // 2

// Remove the alert callback:
removeCallback(alertCallback);

// No alerts:
setGlobal({ value: 3 });
setGlobal({ value: 4 });
resetGlobal

Use resetGlobal to reset the global state. This resets all state values, including callbacks, property listeners, and reducers.

There are no parameters.

import { getGlobal, resetGlobal, setGlobal } from 'reactn';

// Set the value.
setGlobal({ value: 1 });

// Get the value.
alert(getGlobal().value); // 1

// Reset the global state.
resetGlobal();

// Get the value.
alert(getGlobal().value); // undefined
setGlobal

Use setGlobal to initialize or update your global state. This is analogous to calling this.setGlobal in a class component or useGlobal()[1] in a function component.

The first parameter is merged into the global state in the same way a class component's this.setGlobal merges its first parameter into the local state.

The optional second parameter is a callback.

setGlobal with a new global state:

import { setGlobal } from 'reactn';

// Set loading to true.
setGlobal({
  loading: true,
});

setGlobal with a new global state and a callback:

import { setGlobal } from 'reactn';

// Set loading to true.
setGlobal(
  {
    loading: true,
  },

  // After it is set, assert that loading is true.
  global => {
    assert(global.loading === true);
  },
);
useDispatch

Requires React >= 16.8.0

The useDispatch helper function is a React Hook analogous to the useReducer hook built into React itself. useDispatch will dispatch a global reducer that has been added to ReactN via the addReducer, addReducers, or withInit helper functions or a global reducer that you specify inline as a parameter.

useDispatch()

useDispatch() with no parameters will return a map of all of your global reducers.

import { useDispatch } from 'reactn';

function MyComponent() {
  const dispatch = useDispatch();
  dispatch.add(1);
  dispatch.substract(2);
  return null;
}
useDispatch(Function)

useDispatch(f) allows you to define your global reducer inline. This method is particularly useful if you prefer to import your reducers as needed or keep your singleton reducers with the components that use them.

import React, { useDispatch, useGlobal } from 'reactn';

function MyComponent() {
  const [count] = useGlobal('count');
  const add = useDispatch((global, _dispatch, n) => ({
    count: global.count + n,
  }));
  return <button onClick={() => add(1)}>{count}.</span>;
}
useDispatch(Function, keyof State)

useDispatch(f, 'property') allows you to define your global property reducer inline. A property reducer changes only one property of the global state, which can greatly simplify your reducer logic.

import React, { useDispatch, useGlobal } from 'reactn';

function MyComponent() {
  const [count] = useGlobal('count');
  const add = useDispatch((count, n) => count + n, 'count');
  return <button onClick={() => add(1)}>{count}.</span>;
}
useDispatch(keyof Reducers)

useDispatch('reducerName') allows you to dispatch a global reducer.

import React, { useDispatch, useGlobal } from 'reactn';

function MyComponent() {
  const [count] = useGlobal('count');
  const add = useDispatch('add');
  return <button onClick={() => add(1)}>{count}.</span>;
}
useGlobal

Requires React >= 16.8.0

useGlobal is a React Hook analogous to the useState Hook built into React itself. useGlobal returns the global state or parts thereof.

useGlobal()

useGlobal() with no parameters will return the entire global state object and a function for changing properties of the global state.

The setGlobal function returned by useGlobal is analogous to the setGlobal helper function and this.setGlobal class method.

import React, { useGlobal } from 'reactn';

function MyComponent() {
  const [ global, setGlobal ] = useGlobal();
  const generateNumber = () => {
    setGlobal(g => ({
      generations: g.generations + 1,
      myNumber: Math.floor(Math.random() * 100),
    });
  };
  return (
    <button onClick={generateNumber}>
      #{global.generations}: {global.myNumber}
    </button>
  );
}
useGlobal(keyof State)

useGlobal('property') returns a specific global state property and a function for updating that property.

import React, { useGlobal } from 'reactn';

const getRandomNumber = () => Math.floor(Math.random() * 100);

function MyComponent() {
  const [myNumber, setMyNumber] = useGlobal('myNumber');
  return (
    <button onClick={() => setMyNumber(getRandomNumber())}>{myNumber}</button>
  );
}
withGlobal

Use withGlobal to return a higher-order component to convert global state values into props. This is highly analogous to react-redux's connect function.

The first parameter is a function for getting global state values.

The second parameter is a function for setting global state values (similar to dispatch).

import React, { withGlobal } from 'reactn';

// A button that displays the value and, when clicked, increments it.
function MyComponent(props) {
  return (
    <>
      My value is <button onClick={props.incrementValue} value={props.value} />
    </>
  );
}

export default withGlobal(
  // Set the `value` prop equal to the global state's `value` property.
  global => ({
    value: global.value,
  }),

  // Important Note: This is not the setGlobal helper function.
  // Set the `incrementValue` prop to a function that increments the global
  //   state's `value` property.
  setGlobal => ({
    incrementValue: () => {
      // Important Note: This is not the setGlobal helper function.
      // This is the parameter referenced 4 lines up.
      setGlobal(global => ({
        value: global.value + 1,
      }));
    },
  }),
)(MyComponent);
withInit

In some cases (such as when using Next.js), you may be unable to run a setup script prior to your ReactN components mounting. In order to instantiate your global state and reducers prior to mounting, you may use the withInit Higher Order Component. This HOC will await the setting of your global state before mounting the provided Lower Order Component (e.g. <App />).

import React, { useDispatch, useGlobal, withInit } from 'reactn';

const INITIAL_REDUCERS = {
  addOne: ({ count }) => ({
    count: count + 1,
  }),
};

const INITIAL_STATE = {
  count: 0,
};

export default withInit(
  INITIAL_STATE,
  INITIAL_REDUCERS,
)(function App() {
  const addOne = useDispatch('addOne');
  const [count] = useGlobal('count');
  return <button onClick={addOne}>Count: {count}</button>;
});

Known issues

  • super(props) is incompatible with TypeScript. #126
  • Components re-render once per changed subscribed property. #129
  • Class components use componentWillUpdate without the UNSAFE_ prefix. #134
  • Class components are incompatible with Providers in newer versions of React. #132

Terminology

ReactN strictly maintains accurate terminology for its data structures. The majority of ReactN's data structures are meant to be black box to simplify the user experience, only referenced by name in the package's code. They are outlined here for transparency and to ease community contributions.

Dispatcher

When you pass a reducer to ReactN via addReducer, addReducers, useDispatch, or withInit, ReactN returns a dispatcher.

A dispatcher is a function that wraps a reducer, passing the global state and global reducers as parameters tying its return value to the global state. Dispatchers and reducers have a 1-to-1 relationship and are tightly bound to each other.

In documentation, dispatchers are often referred to as reducers to decrease the cognitive overhead and conceptually strengthen their 1-to-1 relationship.

For example, an "add" reducer may be defined as follows:

function add(global, _dispatch, n) {
  return { count: global.count + n };
}

When you call this reducer, you only need to call add(1). This difference in call signature is because you are calling the dispatcher.

A dispatcher, in pseudo-code, conceptually looks as follows:

function dispatchAdd(n) {
  const { dispatchers, set, state } = globalStateManager;
  const newGlobalState = add(state, dispatchers, n);
  return set(newGlobalState);
}

Global state manager

The global state manager is the core object that powers ReactN. It maintains the state, global dispatchers, and subscriptions.

Default global state manager

The default global state manager is the global state manager used by all of ReactN unless otherwise specified. To specify a different global state manager, you must use a Provider.

ReactN Components and Hooks will attempt to find a global state manager via the Context. If one does not exist via Context, it will fallback to the default global state manager.

Reducer

A reducer is a function that accepts the current global state, a map of all global reducers, and any number of additional parameters. A reducer returns a change to the global state. It does not need to return the entire new global state. It only needs to return key-value pairs of changed properties.

An example "add" reducer may be defined as follows:

function add(global, _dispatch, n) {
  return { count: global.count + n };
}

A reducer may be asynchronous (return a Promise) and asynchronously dispatch other reducers. You can use a reducer that dispatches other reducers to create a "saga" of state changes.

async function mySaga(global, dispatch, shouldMultiply) {
  if (global.count < 0) {
    await dispatch.add(1);
  }
  await dispatch.subtract(2);
  if (shouldMultiply) {
    await dispatch.multiply(3);
  }
}

mySaga(true); // shouldMultiply = true

Property reducer

A property reducer is a reducer that only changes one property. They only receive that property's value as a parameter instead of the entire global state object, and they do not receive the dispatch object as a parameter at all.

An example "add" property reducer may be defined as follows:

function add(count, n) {
  return count + n;
}

You must specify the property when using a property reducer. Property reducers cannot be added to or remembered by the global state manager.

import React, { useDispatch, useGlobal } from 'reactn';

function add(count, n) {
  return count + n;
}

function MyComponent() {
  const [count] = useGlobal('count');
  // Use the "add" property reducer on the "count" property.
  const dispatch = useDispatch(add, 'count');
  return <button onClick={() => dispatch(1)}>{count}</button>;
}

Sponsor

If you are a fan of this project, you may become a sponsor via GitHub's Sponsors Program.

Support

For support, reach out to us on the Reactiflux Discord channel #reactn.

chat

Comments
  • Document how to test a ReactN Component

    Document how to test a ReactN Component

    Hi @CharlesStover ,

    I'm experimenting reactn using useGlobal hook. I'm excited with the simplicity brought by reactn against redux!

    One thing I missed is a general path of how to test things up. Reactn is too fresh and there isn't too much resources out there.

    I started using the useGlobal hook, and I'm testing my components using mocks. I've wrote a mock file __mocks__/reactn.js:

    export const useGlobal = jest.fn()
    

    Suppose I have a component that apply the useGlobal like this:

    const [something, setSomething] = useGlobal('something')
    

    And suppose a correponding test SomeComponent.test.js like:

    const something = 'some-value'
    const setSomething = jest.fn()
    useGlobal.mockImplementationOnce([something, setSomething])
    const wrapper = shallow(<SomeComponent />)
    wrapper.simulate('click')
    expect(setSomething).toBeCalledWith('foo-bar')
    expect(useGlobal).toBeCalledWith(['something']);
    expect(useGlobal.mock.calls.map(call => call[0])).toEqual(['something']);
    

    This can work, but I'm not sure if it is the best approach.

    My question is: what do you do for test the reactn usage? Do you have any plan for test helpers? Do you see them as part of the reactn core?

    documentation 
    opened by michelts 25
  • Console warning when adding reactn to class

    Console warning when adding reactn to class

    Hello, I notice some warnings when adding reactn to a class:

    `componentWillUpdate has been renamed, and is not recommended for use. See https://fb.me/react-async-component-lifecycle-hooks for details.

    • Move data fetching code or side effects to componentDidUpdate.
    • Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run npx react-codemod rename-unsafe-lifecycles in your project source folder.

    Please update the following components: ##ComponentName##`

    How to reproduce:

    Create a new project with create-react-app Create a component `import React, { Component } from 'react'

    export default class Compo extends Component { render() { return (

    I am a Component
    ) } } ` and include it in App.js

    Run -> No warnings change first line to `import React, { Component } from 'reactn' get warning:

    `react-dom.development.js:12449 Warning: componentWillUpdate has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.

    • Move data fetching code or side effects to componentDidUpdate.
    • Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run npx react-codemod rename-unsafe-lifecycles in your project source folder.

    Please update the following components: Compo`

    help wanted 
    opened by umbertoghio 20
  • Components only unsubscribe on update or unmount if they have custom update and unmount implementations.

    Components only unsubscribe on update or unmount if they have custom update and unmount implementations.

    The unsubscribe listener on update and unmount only binds to existing implementations. It does not fire if no implementation exists to which it can bind.

    bug 
    opened by CharlesStover 17
  • Warning in Unmounted Component

    Warning in Unmounted Component

    Hi there,

    after i update reactn from version 1.0.0 to 2.0.1 i get on every page change this warning:

    index.js:xxx Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
    

    This didn't happen before.

    Here is an example of my implementation:

    package.json:

    "react": "^16.8.6",
    "react-router": "^5.0.0",
    "react-router-dom": "^5.0.0",
    "redux": "^4.0.1",
    "reactn": "^2.0.1",
    "typescript": "3.4.5",
    // also the types for each package
    
    // index.tsx
    import React from 'reactn';
    import ReactDOM from 'react-dom';
    import {BrowserRouter} from 'react-router-dom';
    
    // typings made before
    setGlobal({payments: [], invoices: []})
    addReducer('getPayments', (global: State, dispatch: Dispatch) => 
       axios.get('url/payments'...).then((data) => ({payments: data})
    );
    
    addReducer('getInvoices', (global: State, dispatch: Dispatch) => 
       axios.get('url/invoices'...).then((data) => ({invoices: data})
    );
    
    ReactDOM.render(
            <BrowserRouter>
                    <App />
                </BrowserRouter>,
            document.getElementById('root')
        );
    
    // App.tsx
    import React from 'reactn';
    
    const Payments = props => {
      const [payments] = useGlobal('payments');
      return <div>....</div>;
    }
    
    const Invoices = props => {
      const [invoices] = useGlobal('invoices');
      return <div>....</div>;
    }
    
    class App extends React.Component {
      render() {
        return (
          <div>
             <Payments />
             <InvoiceList />
          </div>
        );
      }
    }
    

    Not sure if this is related to https://github.com/CharlesStover/reactn/issues/28

    Thanks for your help

    bug 
    opened by r1ckyrockz 17
  • Implement global state life cycle methods.

    Implement global state life cycle methods.

    Hi,

    I have experiment this behavior :

    • I use this.global.x in componentDidUpdate in component A
    • When component B updates global.x, component A doesn't rerender
    • I make something else to make component A render
    • Then when component B updates global.x it does make component A render

    I suppose that before the first call of componentDidUpdate, we don't know that component A is using global.x, so it is not rerendered until something make it call componenetDidUpdate, at this time we see that component A uses global.x.

    I am not sure my issue is clear, is it the expected behavior or do you think it can be changed ?

    Thank you

    feature 
    opened by slevy85 14
  • Components not always re-rendering when using useGlobal() without an argument.

    Components not always re-rendering when using useGlobal() without an argument.

    I am encountering an issue where a component is not always re-rendering correctly depending on how I access a state property. For example, if I have a property foo on my global state or a provider, there are two ways to access it:

    "direct" way:

    const [foo, setFoo] = useGlobal('foo');
    //then use "foo"
    

    "indirect" way:

    const [state, setState] = useGlobal();
    //then use "state.foo"
    

    Under some circumstances, the "indirect" way won't cause the component to re-render. And oddly, just by merely adding a declaration of the state property the "direct" way, all of the issues go away, without changing any of the references.

    Also, it seems there are some differences in outcome based on whether we are using a dispatcher vs. manually updating the state. But not always... (I know).

    Also, it seems that simply updating any regular React vanilla useState value in the component will cause a re-render, and all of the state updates we were not seeing magically appear.

    So, if you're a bit confused, don't worry, I was too. I have spent hours narrowing this down, and thankfully I was finally able to repro it very simply in a code sandbox:

    https://codesandbox.io/s/reactn-bug-z7ebn

    Let me know if you have any questions, but the sandbox lays out all the details.

    Matt

    P.S. I reported this several months ago, but didn't have time to isolate it. So, apologies for the extra ticket.

    bug 
    opened by m4ttheweric 13
  • Support React versions < 16.3.0

    Support React versions < 16.3.0

    Throws error at Index.js (line 105):

    "TypeError: object(...) is not a function"

    when importing reactn in react version 16.0.0. When upgrading react to newest version (16.8.6), I was able to import without issues. Took me quite some time to figure it out since it says in your FAQ that reactn supports all react versions across the board. Apparently that's not the case.

    feature 
    opened by janezk7 13
  • Prevent forceUpdate on components without a render method

    Prevent forceUpdate on components without a render method

    Hi @CharlesStover, thanks for your great work. I think it could be useful to prevent forceUpdate invocation on Components that doesn't have a render method. Something like this seems to be enough:

    function ReactNGlobalCallback(_this) {
    	if (typeof _this['render'] === 'function') {
    		_this.updater.enqueueForceUpdate(_this, null, 'forceUpdate');
    	}
    }
    

    Nevertheless, I suppose there's a better way to handle this situation, provided that it makes sense to you.

    opened by syron0 11
  • Warning in Unmounted Component (react-router)

    Warning in Unmounted Component (react-router)

    Hi there, I currently have a Login Component, and based on the server's response I will call (react-router-dom) <Redirect to='/dashboard' /> if the server responds with affirmation the user has a valid session cookie already. However, I get this warning inside my component:

    Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
        in Login (created by LoginBox)
        in div (created by Card)
        in div (created by Card)
        in Card (created by LoginBox)
        in LoginBox (created by Route)
    

    To clarify, the setState is already done by the time the redirect is called:

    // Hooks, Handlers, etc.
    ...
    if (authorized) {
            return <Redirect to='/dashboard' />
    }
    // else, Display Login Component
    ...
    

    From the Warning, I get a hint that something should be cleaned up? Not sure if this is related to https://github.com/CharlesStover/reactn/issues/5.

    bug 
    opened by eeyang92 11
  • Feature request: add previous global state to addCallback parameters

    Feature request: add previous global state to addCallback parameters

    First of all let me tell you how I love this library. It is simple, powerful and made my work much, much better and enjoyable. Thanks mate!

    To the point now.

    I am having a case where I would like to call a certain function whenever I update the global state, but only when a particular property of the state has been changed.

    I thought that the addCallback helper function could help me. The thing is that I cannot compare the new state with the previous one. Is it difficult to add the prevGlobal as the second parameter of the addCallback helper function?

    Or maybe there is some other approach I did not think of?

    opened by monteiz 10
  • Error: React v16.7 or newer is required.

    Error: React v16.7 or newer is required.

    Error: React v16.7 or newer is required at t.exports /reactn/index.js:1:12795

    Issue occured after updating ^16.7.0-alpha.0 > ^16.7.0

    Tested

    • Removing / Installing node_packages = 👎
    • Removing yarn lock file = 👎
    • Downgrading to '16.7.0-alpha.0' = 👎

    So now am confused what the cause of this issue could be. Could be something locally for me even if i did multiple modules wipe / install runs.

    opened by Mindgames 9
  • Dependency Use force update 1.0.10 causes a Type Error

    Dependency Use force update 1.0.10 causes a Type Error

    It seems that the dependency 'use-force-update 1.0.10' throws a Type error when using the useGlobal hook: Uncaught TypeError: use_force_update_1.default is not a function

    Rolling back to use-force-update v1.0.8 fixes the issue.

    opened by jessesward 7
  • Bump eventsource from 1.0.7 to 1.1.1 in /docs

    Bump eventsource from 1.0.7 to 1.1.1 in /docs

    Bumps eventsource from 1.0.7 to 1.1.1.

    Changelog

    Sourced from eventsource's changelog.

    1.1.1

    • Do not include authorization and cookie headers on redirect to different origin (#273 Espen Hovlandsdal)

    1.1.0

    • Improve performance for large messages across many chunks (#130 Trent Willis)
    • Add createConnection option for http or https requests (#120 Vasily Lavrov)
    • Support HTTP 302 redirects (#116 Ryan Bonte)
    • Prevent sequential errors from attempting multiple reconnections (#125 David Patty)
    • Add new to correct test (#111 Stéphane Alnet)
    • Fix reconnections attempts now happen more than once (#136 Icy Fish)
    Commits
    • aa7a408 1.1.1
    • 56d489e chore: rebuild polyfill
    • 4a951e5 docs: update history for 1.1.1
    • f9f6416 fix: strip sensitive headers on redirect to different origin
    • 9dd0687 1.1.0
    • 49497ba Update history for 1.1.0 (#146)
    • 3a38537 Update history for #136
    • 46fe04e Merge pull request #136 from icy-fish/master
    • 9a4190f Fix issue: reconnection only happends for 1 time after connection drops
    • 61e1b19 test: destroy both proxied request and response on close
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump url-parse from 1.4.7 to 1.5.10 in /docs

    Bump url-parse from 1.4.7 to 1.5.10 in /docs

    Bumps url-parse from 1.4.7 to 1.5.10.

    Commits
    • 8cd4c6c 1.5.10
    • ce7a01f [fix] Improve handling of empty port
    • 0071490 [doc] Update JSDoc comment
    • a7044e3 [minor] Use more descriptive variable name
    • d547792 [security] Add credits for CVE-2022-0691
    • ad23357 1.5.9
    • 0e3fb54 [fix] Strip all control characters from the beginning of the URL
    • 61864a8 [security] Add credits for CVE-2022-0686
    • bb0104d 1.5.8
    • d5c6479 [fix] Handle the case where the port is specified but empty
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump follow-redirects from 1.13.0 to 1.14.8 in /docs

    Bump follow-redirects from 1.13.0 to 1.14.8 in /docs

    Bumps follow-redirects from 1.13.0 to 1.14.8.

    Commits
    • 3d81dc3 Release version 1.14.8 of the npm package.
    • 62e546a Drop confidential headers across schemes.
    • 2ede36d Release version 1.14.7 of the npm package.
    • 8b347cb Drop Cookie header across domains.
    • 6f5029a Release version 1.14.6 of the npm package.
    • af706be Ignore null headers.
    • d01ab7a Release version 1.14.5 of the npm package.
    • 40052ea Make compatible with Node 17.
    • 86f7572 Fix: clear internal timer on request abort to avoid leakage
    • 2e1eaf0 Keep Authorization header on subdomain redirects.
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump ajv from 6.10.0 to 6.12.6 in /docs

    Bump ajv from 6.10.0 to 6.12.6 in /docs

    Bumps ajv from 6.10.0 to 6.12.6.

    Release notes

    Sourced from ajv's releases.

    v6.12.6

    Fix performance issue of "url" format.

    v6.12.5

    Fix uri scheme validation (@​ChALkeR). Fix boolean schemas with strictKeywords option (#1270)

    v6.12.4

    Fix: coercion of one-item arrays to scalar that should fail validation (failing example).

    v6.12.3

    Pass schema object to processCode function Option for strictNumbers (@​issacgerges, #1128) Fixed vulnerability related to untrusted schemas (CVE-2020-15366)

    v6.12.2

    Removed post-install script

    v6.12.1

    Docs and dependency updates

    v6.12.0

    Improved hostname validation (@​sambauers, #1143) Option keywords to add custom keywords (@​franciscomorais, #1137) Types fixes (@​boenrobot, @​MattiAstedrone) Docs:

    v6.11.0

    Time formats support two digit and colon-less variants of timezone offset (#1061 , @​cjpillsbury) Docs: RegExp related security considerations Tests: Disabled failing typescript test

    v6.10.2

    Fix: the unknown keywords were ignored with the option strictKeywords: true (instead of failing compilation) in some sub-schemas (e.g. anyOf), when the sub-schema didn't have known keywords.

    v6.10.1

    Fix types Fix addSchema (#1001) Update dependencies

    Commits
    • fe59143 6.12.6
    • d580d3e Merge pull request #1298 from ajv-validator/fix-url
    • fd36389 fix: regular expression for "url" format
    • 490e34c docs: link to v7-beta branch
    • 9cd93a1 docs: note about v7 in readme
    • 877d286 Merge pull request #1262 from b4h0-c4t/refactor-opt-object-type
    • f1c8e45 6.12.5
    • 764035e Merge branch 'ChALkeR-chalker/fix-comma'
    • 3798160 Merge branch 'chalker/fix-comma' of git://github.com/ChALkeR/ajv into ChALkeR...
    • a3c7eba Merge branch 'refactor-opt-object-type' of github.com:b4h0-c4t/ajv into refac...
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
Releases(v2.2.7)
  • v2.2.7(Apr 4, 2020)

  • v2.2.6(Feb 18, 2020)

    Bug fixes 🐛

    • Fixes issue where useGlobal() hook with no parameters would sometimes not trigger a re-render on state changes after the first. #150 (Thanks @m4ttheweric!)

    Miscellaneous 📃

    • Upgrades dev dependencies to latest, allowing asynchronous act. 👍
      • Deprecated unit tests for non-latest versions of React, because they do not support asynchronous act. 😢
    Source code(tar.gz)
    Source code(zip)
  • v2.2.5(Jan 31, 2020)

    2.2.5

    • Removes use of deprecated componentDidUnmount for class components. #134

    2.2.3/2.2.4

    • Batches subscriptions using ReactDOM. #129
    • Reverted due to lack of React Native support.

    2.2.2

    • Memoizes useGlobal's setter function. #123

    2.2.1

    • Fixes TypeScript issue where map of dispatchers was not extensible.
    Source code(tar.gz)
    Source code(zip)
  • v2.2.0(Aug 18, 2019)

    New Features

    • Global reducers now receive a dispatch function in addition to the dispatch object. This dispatch function asynchronously calls setGlobal and returns the new global state, allowing you to await the change. This is the first step towards sagas. #116
    function myReducer(global, dispatch, ...params) {
      await dispatch({ change: 1 }); // <-- dispatch is a function
      await disaptch.someOtherReducer(params); // <-- dispatch is a map
      await dispatch({ change 2 });
    }
    
    Source code(tar.gz)
    Source code(zip)
  • v2.1.6(Jul 21, 2019)

    New Features ✨

    • ReactN now supports all versions of React >= 0.14. #60
      • Thanks @janezk7 and @davidrenne!

    Bug Fixes 🐛

    • Fixed withGlobal using the default global state instead of a Provider's global state for Components inside a Provider in versions of React >=16.3 <16.6.

    Miscellaneous

    • Upgraded react-testing-library to @testing-library/react.

    • Added documentation for useDispatch and useGlobal to the README.

    Source code(tar.gz)
    Source code(zip)
  • v2.1.5(Jul 20, 2019)

    New Features ✨

    • Added a withInit HOC that initializes the global state and its reducers. #84
      • Thanks to the ReactN Discord channel and @ZinoKader!

    Bug Fixes 🐛

    • [TypeScript] Reducers added via addReducer now match their definitions in reactn/default's Reducers interface. #105
      • Thanks @MikeMeyers2504!
    Source code(tar.gz)
    Source code(zip)
  • v2.1.3(Jun 10, 2019)

    Bug Fixes 🐛

    • Fixed class components only unsubscribing from a single property on unmount. #85
      • Massive thanks to @umbertoghio for providing two repositories to reproduce this!

    New Features ✨

    • The dispatch function returned by useDispatch, when providing a property reducer and property name, can now be destructured. #90
      • This behavior is analogous to React's native useReducer behavior.
      • const [ value, dispatch ] = useDispatch(propertyReducerFunction, propertyName);

    Miscellaneous

    • A TypeScript PropertyDispatcher type has been added to reactn/types/dispatcher, referencing a dispatcher that can be destructured.
    Source code(tar.gz)
    Source code(zip)
  • v2.1.2(Jun 4, 2019)

  • v2.1.1(Jun 4, 2019)

    New Features ✨

    • Allows you to specify a global state property name when using useDispatch with a function. This behaves closely to how React's native useReducer works. #82
    import React, { useDispatch, useGlobal } from 'reactn';
    
    const INITIAL_COUNT= 0;
    
    setGlobal({ count: INITIAL_COUNT});
    
    const doMath = (count, action) {
      switch (action.type) {
        case 'ADD' :
          return count + action.value;
        case 'SUBTRACT':
          return count - action.value;
        case 'RESET':
          return INITIAL_COUNT;
        default:
          return count;
      }
    };
    
    function MyComponent() {
      const [ count] = useGlobal('count');
      const dispatchMath = useDispatch(doMath, 'count'); // <-- use doMath to modify count
      return <>
        <button onClick={() => dispatchMath({ type: 'ADD', value: 1 })}>
          Add 1
        </button>
        <button onClick={() => dispatchMath({ type: 'SUBTRACT', value: 3 })}>
          Subtract 3
        </button>
        <button onClick={() => dispatchMath({ type: 'RESET' })}>
          Reset
        </button>
        <strong>Count:</strong> {count}
      </>;
    }
    

    Miscellaneous 📃

    • Fixed useDispatch parameters in README not having been updated to include dispatch in 2.x. #87 (Thanks @yezyilomo!)
    • Added ReactN DevTools to documentation. #80
    Source code(tar.gz)
    Source code(zip)
  • v2.0.4(Jun 1, 2019)

    Breaking Changes 💔

    The follow breaking change to withGlobal was not deemed worthy of a major version bump, because it should have been included in 2.0.0.

    • The getter and setter function parameters are no longer of type (global, props) => ... and (setGlobal, props) => ... respectively.
      • Both now accept the dispatch object as a second parameter, which contains and dispatches your global reducers.
    • The getter function is now of type (global, dispatch, props) => ....
    • The setter function is now of type (global, dispatch, props) => ....

    Before:

    export default withGlobal(
      (global, props) => ({ ... }),
      (setGlobal, props) => ({ ... }),
    )(MyComponent);
    

    After:

    export default withGlobal(
      (global, dispatch, props) => ({ ... }),
      (setGlobal, dispatch, props) => ({ ... }),
    )(MyComponent);
    

    Bug Fixes 🐛

    • withGlobal is now fixed on React Native when there is no Provider. #78 (Thanks @Brianop, @BDQ!)
      • Unlike React for Web, React Native returns a truthy Context when the Context is missing.
      • This was erroneously resulting in ReactN believing it had the global state when it did not.

    Miscellaneous 📄

    • Added unit tests for withGlobal to validate that it works with a Context, without a Context, and via a Provider. #66
    • Fixed a TypeScript error that Provider.withGlobal() required parameters, when they are optional.
    • Moved the ReactN Provider type to 'reactn/types/provider'.
    • Moved the useGlobal types to 'reactn/types/use-global'.
    • Moved the withGlobal types to 'reactn/types/with-global'.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.3(Jun 1, 2019)

    Bug Fixes 🐛

    • Fixed class Components not unsubscribing from state changes on unmount and update. #76 #85 (Thanks @r1ckyrockz, @bugs181, @umbertoghio, @stahlmanDesign!)
      • Special thanks to @umbertoghio in depth logging and screenshots.
    • Attempt to prevent Webstorm's auto-imports from importing from the wrong file. #74

    Miscellaneous

    • reactn/typings/* directory renamed to reactn/types/*.
    • package-lock.json added to the .gitignore and .npmignore files, allowing contributors to install with NPM instead of Yarn.
    • yarn upgrade performed on the repository root and docs directories (updating their respective yarn.lock files).
    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(May 23, 2019)

    "Breaking" Changes 💔

    • ReactN DevTools were moved to a separate package.
      • This only resulted in a minor version bump, because this change only breaks a tool in the development environment.
      • Builds, both development and production, remain unbroken.

    New Features ✨

    • Callbacks now receive the state change in addition to the new state.
    • Callbacks now receive the reducer name and arguments that resulted in the state change (if applicable).

    Bug Fixes 🐛

    • Builds no longer warn about a missing optional Redux dependency. #71 #79 (Thanks @ryanvanderpol, @chrise86!)

    Miscellaneous

    • Added some placeholder methods for potential future implementations of middleware.
    • Some TypeScript definitions that were missing from the final build are now included.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(May 15, 2019)

    New Features

    • Adds a getDispatch helper function to return all dispatch functions (created from reducers) currently attached to the global state. #73

    • Allows useDispatch to be called without any parameters to return all dispatch functions currently attached to the global state.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(May 10, 2019)

    Since its inception, ReactN has always felt to me to be deeply tied to the community. It is likely the first time I started a project with intense community interviewing before ever writing the first line of code. Each step of the way, I tried to thoroughly document my and developers' thought processes and use cases in the most public ways possible -- GitHub Issues, Medium articles, Reddit threads, and even now a Discord channel on the Reactiflux server.

    ReactN spent long enough evolving in v0 that 1.0.0 never even needed so much as a patch.

    The first change since launch is a breaking change, so 2.0.0 launched today. I wanted to engage the community with this change.

    Breaking Changes 👷‍♂️

    • Global state and reducers are now on different member variables and hooks.
    // Class Component member variables
    this.global.property; // -->
    this.global.property; // (unchanged)
    
    this.global.reducer('value'); // -->
    this.dispatch.reducer('value');
    
    // Function Component hooks
    useGlobal('property'); // -->
    useGlobal('property'); // (unchanged)
    
    useGlobal('reducer'); // -->
    useDispatch('reducer');
    
    useGlobal(function(){ }); // -->
    useDispatch(function(){ });
    
    • Global reducers now receive the dispatch object as a function parameter.
    // Previous global reducer:
    function reducer(state, arg1, arg2) { }
    
    // Current global reducer:
    function reducer(state, dispatch, arg1, arg2) { }
    

    Feature Additions 🏢

    • Full TypeScript support. Intellisense has never been so powerful.

    • Redux DevTools are supported! View your ReactN state and dispatched actions right from developer tools. #41

    • Ability to type the default global state. (Example) #50

    • Reducers as sagas, which is why reducers now receive the dispatch object. You can now have a reducer that dispatches other reducers. #62

    function addSubtract(state, dispatch, a, s) {
      await dispatch.add(a);
      await dispatch.subtract(s);
    }
    

    Bug Fixes 🐛

    • Slight re-render optimization. #5

    Next Steps 💭

    Since ReactN serves as both a React-integrated API and a global state, I was thinking it would be a non-breaking change (exact same API; semver 2.1) to change the global state manager to a Redux store. This could improve support for third party tooling (like middleware) while offsetting a lot of testing and maintenance. Just toying with the idea. I don't want the use of Redux to decrease user confidence that boilerplate is eliminated or learning curve is shallow. This would essentially make ReactN an alternative to react-redux instead of redux. I'll let the community discuss and focus more on a smooth 2.0 launch for the time being.

    Thanks for all the support! 🎉

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Feb 18, 2019)

    Features

    • Added a Context-based Provider Component for managing multiple global states simultaneously. #17
    • Added helper functions to Provider Components for targeting a global state. #37
    • Added iterator logic to reducers to support [ state, dispatch ] destructuring. #42
    Source code(tar.gz)
    Source code(zip)
  • v0.2.1(Feb 11, 2019)

  • v0.2.0(Feb 4, 2019)

    Features

    • Added addCallback to execute a function after global state changes. #29

    • Added getGlobal helper function for accessing a snapshot of the current global state. #35

    • Added removeCallback to remove callbacks added via addCallback.

    Miscellaneous

    • Added unit tests for most helper functions.

    • Ported the singleton global state to a default global state, in preparation for multiple global states via the Context API.

    Source code(tar.gz)
    Source code(zip)
  • v0.1.8(Dec 12, 2018)

  • v0.1.7(Dec 10, 2018)

  • v0.1.2(Nov 27, 2018)

  • v0.1.1(Nov 21, 2018)

    Features

    • Adds resetGlobal as a helper function. #2

    • Adds setGlobal as a second parameter to the withGlobal HOC. #13

    const GlobalComponent = withGlobal(
      (global, props) => null,
      (setGlobal, props) => null
    )(LocalComponent);
    

    This is analogous to state/dispatch and mapStateToProps/mapDispatchToProps as used in the react-redux package.

    Bug Fix

    • Fixes unnecessary re-renders that were occurring on some occasions. #21

    Miscellaneous

    • Unit test for createReducer added.

    • Unit tests for component methods prepped.

    • Unit tests for helper functions prepped.

    Source code(tar.gz)
    Source code(zip)
Owner
Charles Stover
Senior front end engineer at AWS
Charles Stover
A tiny and unobtrusive state management library for React and Preact apps

statty A tiny and unobtrusive state management library for React and Preact apps The current size of statty/dist/statty.umd.min.js is: The problem Mos

Alessandro Arnodo 515 Dec 20, 2022
experimental project for babel-plugin-mutable-react-state

Goalist Mutable React Example This example is an attempt to work with babel-plugin-mutable-react-state on a simpler project to see how complicated wou

Reaper 1 Jun 7, 2022
The state manager ☄️

☄️ effector The state manager Table of Contents Introduction Effector follows five basic principles: Installation Documentation Packages Articles Comm

effector ☄️ 4k Jan 2, 2023
Create the next immutable state by mutating the current one

Immer Create the next immutable state tree by simply modifying the current tree Winner of the "Breakthrough of the year" React open source award and "

immer 24.3k Jan 4, 2023
✍️ Immutable state with a mutable API

react-copy-write An immutable React state management library with a simple mutable API, memoized selectors, and structural sharing. Powered by Immer.

Brandon Dail 1.8k Dec 10, 2022
🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️

A reactive database framework Build powerful React and React Native apps that scale from hundreds to tens of thousands of records and remain fast ⚡️ W

Nozbe 8.8k Jan 8, 2023
An immutable data store for managing deeply nested structure with React

Cortex is an immutable data store for managing deeply nested structure with React Key features: supports deeply nested data uses immutable data, which

Quan Nguyen 1.1k Dec 16, 2022
Highly Composable MVVM Framework for React

Astarisx Highly Composable MVVM Framework for React. Click here for the Astarisx Website and Documentation Highly Composable UI Components Astarisx Vi

Zuudo 86 Aug 13, 2019
Async rendering & data-fetching for universal React applications.

React Resolver Async-rendering & data-fetching for universal React applications. React Resolver lets you define data requirements per-component and wi

Eric Clemmons 1.7k Jan 3, 2023
React integration for Baobab.

baobab-react Welcome to baobab's React integration (from v2.0.0 and onwards). Implemented patterns: Hooks Higher order components (curried so also usa

Guillaume Plique 309 Sep 29, 2022
Immutable data structures with history for top-to-bottom properties in component based libraries like React. Based on Immutable.js

Immstruct A wrapper for Immutable.js to easily create cursors that notify when they are updated. Handy for use with immutable pure components for view

null 376 Nov 10, 2022
A library for writing React components that automatically manage subscriptions to data sources simply by accessing them

ReSub A library for writing better React components and data stores. Uses automatic subscriptions to reduce code and avoid common data flow pitfalls.

Microsoft 613 Dec 23, 2022
A lightweight react global state management library

Reate A lightweight react global state management library. ✨ Feature Simple and easy to use, only three APIs

青湛 19 Oct 26, 2021
An event-based global state management tool for vue, react, mini-program, ect.

hy-event-store An event-based global state management tool for vue, react, mini-program, etc. 一个基于事件的全局状态管理工具,可以在Vue、React、小程序等任何地方使用。 设计灵感 在项目中找到一个更加

null 260 Jan 2, 2023
In this demo, I am using a library created by me, called 'aesthetic-state' for global state managment in React

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Dany Beltran 2 Oct 21, 2021
The simple but very powerful and incredibly fast state management for React that is based on hooks

Hookstate The simple but very powerful and incredibly fast state management for React that is based on hooks. Why? • Docs / Samples • Demo application

Andrey 1.5k Jan 3, 2023
The simple but very powerful and incredibly fast state management for React that is based on hooks

Hookstate The simple but very powerful and incredibly fast state management for React that is based on hooks. Why? • Docs / Samples • Demo application

Andrey 1.5k Jan 7, 2023
Supercharge your react app with simple Flux-pattern based Global State, using react owns `useReducer` and eliminate the need for redux!

Supercharge your react app with simple Flux-pattern based Global State, using react owns `useReducer` and eliminate the need for redux!

EthicDevs 2 Aug 26, 2022
Simple global state for React with Hooks, which just depends on React's useEffect and useState.

react-hooks-simple-global-state Simple global state for React with Hooks, which just depends on React's useEffect and useState. The idea here is simpl

Ayrton Everton 4 Aug 5, 2022
Simple global state for React with Hooks API without Context API

react-hooks-global-state Simple global state for React with Hooks API without Context API Introduction This is a library to provide a global state wit

Daishi Kato 964 Jan 7, 2023