Redux middleware that helps you load redux initial state asynchronously

Overview

Redux Async Initial State

Redux middleware for async loading of initial app state.

build status npm version

npm install --save redux-async-initial-state

What? Why?

With redux it is quite simple to synchronously load initial state, i.e. from localStorage:

...
const initialState = JSON.parse(localStorage.getItem('state'));
const store = storeCreator(reducer, initialState);

But it becomes quite complicated to do it asynchronously, i.e. from server or from async storage, like in React Native. This middleware do it for you.

Usage

  1. Add package
npm install --save redux-async-initial-state
  1. Change your reducer and add middleware:

before:

import { createStore, combineReducers } from 'redux'
import * as reducers from 'reducers'

const reducer = combineReducers(reducers)
const store = createStore(reducer)

After

import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import * as reducers from 'reducers';
import * as asyncInitialState from 'redux-async-initial-state';

// We need outerReducer to replace full state as soon as it loaded
const reducer = asyncInitialState.outerReducer(combineReducers({
  ...reducers,
  // We need innerReducer to store loading state, i.e. for showing loading spinner
  // If you don't need to handle loading state you may skip it
  asyncInitialState: asyncInitialState.innerReducer,
}));

// Load state function
// Should return promise that resolves application state
const loadStore = () => {
  return new Promise(resolve => {
    fetch('/store')
      .then(response => response.json())
      .then(resolve);
  });
}

const store = createStore(
  reducer,
  compose(applyMiddleware(asyncInitialState.middleware(loadStore)))
);

Partial replace

In case when you're loading only part of your store initially, you can add getCurrentState argument in loadStore function. So, if you have some complex shape of your reducer and you need to replace only some of keys in your store (currentUser in example below):

const loadStore = (getCurrentState) => {
  return new Promise(resolve => {
    fetch('/current_user.json')
      .then(response => response.json())
      .then(user => {
        resolve({
          // reuse state that was before loading current user
          ...getCurrentState(),
          // and replace only `currentUser` key
          currentUser: user
        })
      });
  });
}

Reducer

The shape of innerReducer is:

{
  loaded: false,
  loading: false,
  error: false
}

You can add it to you reducer if you want to handle loading state, i.e. to show loading spinner. Here is React example (it uses reducer, described above):

import React from 'react';
import { connect } from 'react-redux';

@connect(state => ({
  loading: state.asyncInitialState.loading,
}))
class MyComponent extends React.Component {
  render() {
    if (this.props.loading) {
      return <div>Loading...</div>
    }
    return ...;
  }
}
Comments
  • Issue #11: Fix out-of-sync state

    Issue #11: Fix out-of-sync state

    Passing getState as a function fixes a race condition that can occur if actions are dispatched between redux-async-initial-state-/STATE_LOADING_START and redux-async-initial-state-/STATE_LOADING_DONE.

    By passing getState as the second parameter to the load function, backwards compatibility is maintained.

    opened by tuntisz 7
  • Actions are dispatched before initial state is loaded

    Actions are dispatched before initial state is loaded

    Hi,

    I have a container component that is connected to redux via connect() function and it has componentDidMount() function that dispatches an action XXX.

    When I load the page, this action XXX is dispatched BEFORE redux-async-initial-state/STATE_LOADING_DONE.

    Is it a correct behavior? Should any action be dispatched (and container's render() method called) before initial state is loaded?

    opened by maks-rafalko 6
  • Not compatible to redux 4.0.0

    Not compatible to redux 4.0.0

    The package can not be used with redux >= 4.0.0. Then I get the following error:

    Dispatching while constructing your middleware is not allowed. Other middleware would not be applied to this dispatch.

    opened by tsdevelopment 5
  • Use with React Native and AsyncStorage

    Use with React Native and AsyncStorage

    I can't seem to get this configured correctly to load from Async Storage. As where do I put in other middleware that I am using. Thanks In advance for your help.

    opened by flamingo-peacock 5
  • Error when using asyncInitialState.outerReducer on first load

    Error when using asyncInitialState.outerReducer on first load

    I keep getting the following Redux error when using the asyncInitialState.outerReducer function in React Native on first load.

    console.error: "The previous state received by the reducer has unexpected type of "Function". Expected argument to be an object with the following keys: "app", "routing", "asyncInitialState""
    

    If I reload the app it works as expected, loading the state from AsyncStorage.

    Here is our configureStore function:

    const appReducers = combineReducers({
        app: app.appSlice,
        routing: routerReducer,
        asyncInitialState: asyncInitialState.innerReducer
    });
    
    const rootReducer = (state, action) => {
        if (action.type === 'AUTH_SIGN_OUT') {
          state = {
            app: app.signOutStatePartial(state)
          };
        }
        return appReducers(state, action);
      };
    
    // This here seems to be the cause of the issue
    const reducer = asyncInitialState.outerReducer(rootReducer);
    
    return createReduxStore(
        reducer,  // setting this to `rootReducer` gets rid of the error
        applyMiddleware(
            asyncInitialState.middleware(loadStore),
            ...
        )
    )
    

    If I change reducer in the createReduxStore to rootReducer the app will load, but won't load the localState from AsyncStorage.

    There seems to be an issue with the asyncInitialState.outerReducer function.

    opened by SMJ93 3
  • Help needed

    Help needed

    Hi, I tried to implement this lib in an empty project with react router, but I did not succeed is there an example anywhere ? (with or without router). Thanks

    opened by JiDai 3
  • Does component get rendered before async state is resolved?

    Does component get rendered before async state is resolved?

    It seems that in use components get rendered initially with an empty store, then re-rendered shortly there after when then async request resolves. Is this the desired behavior?

    If so, how might one account for a situation where they don't want the component to be rendered UNTIL the async request resolves?

    opened by cramatt 2
  • Race conditions occur when passing only the current state

    Race conditions occur when passing only the current state

    If any action occurs between redux-async-initial-state/STATE_LOADING_START and redux-async-initial-state/STATE_LOADING_DONE, currentState becomes out of sync.

    By passing getState instead of getState(), developers are able to grab the latest store on promise resolution. https://github.com/KELiON/redux-async-initial-state/blob/master/src/middleware.js#L7

    opened by tuntisz 2
  • Bump lodash from 4.17.10 to 4.17.19

    Bump lodash from 4.17.10 to 4.17.19

    Bumps lodash from 4.17.10 to 4.17.19.

    Release notes

    Sourced from lodash's releases.

    4.17.16

    Commits
    Maintainer changes

    This version was pushed to npm by mathias, a new releaser for lodash since your current version.


    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] 1
  • Bump lodash from 4.17.10 to 4.17.15

    Bump lodash from 4.17.10 to 4.17.15

    Bumps lodash from 4.17.10 to 4.17.15.

    Commits

    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 ignore this [patch|minor|major] version will close this PR and stop Dependabot creating any more for this minor/major 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] 1
  • If loadStore returns current state, the STATE_LOADING_DONE action never gets called

    If loadStore returns current state, the STATE_LOADING_DONE action never gets called

    It seems if I don't have anything in localStorage, the loadStore returns currentState, but never fires the STATE_LOADING_DONE action.

    const loadStore = (currentState) => {
      return new Promise(resolve => {
        AsyncStorage.getItem('state')
          .then(state => {
            if (state === null) return currentState;
            const parsedState = JSON.parse(state);
            resolve(parsedState);
          });
      });
    }
    
      const appReducers = combineReducers({
       ...reducers
        asyncInitialState: asyncInitialState.innerReducer
      });
    
      const rootReducer = (state, action) => {
        if (action.type === 'AUTH_SIGNOUT') {
          const { initialState } = app;
          state = {
            app: {
              ...initialState,
              persistedData: state.app.persistedData
            }
        }
        return appReducers(state, action);
      }
    
    const reducer = asyncInitialState.outerReducer(rootReducer);
    
    createReduxStore(
        reducer,
        applyMiddleware(
          asyncInitialState.middleware(loadStore),
          thunkMiddleware
        )
      );
    
    opened by conor909 1
  • Bump minimatch and mocha

    Bump minimatch and mocha

    Bumps minimatch to 3.0.4 and updates ancestor dependency mocha. These dependencies need to be updated together.

    Updates minimatch from 0.3.0 to 3.0.4

    Commits
    Maintainer changes

    This version was pushed to npm by isaacs, a new releaser for minimatch since your current version.


    Updates mocha from 2.5.3 to 10.1.0

    Release notes

    Sourced from mocha's releases.

    v10.1.0

    10.1.0 / 2022-10-16

    :tada: Enhancements

    :nut_and_bolt: Other

    v10.0.0

    10.0.0 / 2022-05-01

    :boom: Breaking Changes

    :nut_and_bolt: Other

    Also thanks to @​ea2305 and @​SukkaW for improvements to our documentation.

    v9.2.2

    9.2.2 / 2022-03-11

    Please also note our announcements.

    :bug: Fixes

    ... (truncated)

    Changelog

    Sourced from mocha's changelog.

    10.1.0 / 2022-10-16

    :tada: Enhancements

    :nut_and_bolt: Other

    10.0.0 / 2022-05-01

    :boom: Breaking Changes

    :nut_and_bolt: Other

    Also thanks to @​ea2305 and @​SukkaW for improvements to our documentation.

    9.2.2 / 2022-03-11

    :bug: Fixes

    :nut_and_bolt: Other

    ... (truncated)

    Commits
    • 5f96d51 build(v10.1.0): release
    • ed74f16 build(v10.1.0): update CHANGELOG
    • 51d4746 chore(devDeps): update 'ESLint' to v8 (#4926)
    • 4e06a6f fix(browser): increase contrast for replay buttons (#4912)
    • 41567df Support prefers-color-scheme: dark (#4896)
    • 61b4b92 fix the regular expression for function clean in utils.js (#4770)
    • 77c18d2 chore: use standard 'Promise.allSettled' instead of polyfill (#4905)
    • 84b2f84 chore(ci): upgrade GH actions to latest versions (#4899)
    • 023f548 build(v10.0.0): release
    • 62b1566 build(v10.0.0): update CHANGELOG
    • Additional commits viewable in compare view
    Maintainer changes

    This version was pushed to npm by juergba, a new releaser for mocha since your current version.


    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 lodash from 4.17.10 to 4.17.21

    Bump lodash from 4.17.10 to 4.17.21

    Bumps lodash from 4.17.10 to 4.17.21.

    Commits
    • f299b52 Bump to v4.17.21
    • c4847eb Improve performance of toNumber, trim and trimEnd on large input strings
    • 3469357 Prevent command injection through _.template's variable option
    • ded9bc6 Bump to v4.17.20.
    • 63150ef Documentation fixes.
    • 00f0f62 test.js: Remove trailing comma.
    • 846e434 Temporarily use a custom fork of lodash-cli.
    • 5d046f3 Re-enable Travis tests on 4.17 branch.
    • aa816b3 Remove /npm-package.
    • d7fbc52 Bump to v4.17.19
    • Additional commits viewable in compare view
    Maintainer changes

    This version was pushed to npm by bnjmnt4n, a new releaser for lodash since your current version.


    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
  • Initial state not loaded immediately

    Initial state not loaded immediately

    I currently use store.dispatch({ type: '' }); just after createStore() as a workaround to make STATE_LOADING_START dispatched asap but it's quite ugly. Is it the expected way to do this?

    opened by olivierpascal 4
  • Trouble using enhancers router, thunk

    Trouble using enhancers router, thunk

    I have gotten this to work at a basic level. But, after I add router and thunk enhancers, I get the following error:

    inputState.withMutations is not a function
    TypeError: inputState.withMutations is not a function
    

    Here is roughly my store:

        const reducer = asyncInitialState.outerReducer(rootReducer);
        const initialState = compose(applyMiddleware(asyncInitialState.middleware(loadStore.bind(null, storeUrl))));
        const enhancer = composeEnhancers(applyMiddleware(thunk, router));
        return createStore(
            reducer,
            initialState,
            enhancer
        );
    
    opened by cramatt 2
  • createStore call missing in docs?

    createStore call missing in docs?

    I'm having trouble getting the example to work from the docs. I haven't had time to dig in yet, but I'm wondering if it has anything to do with createStore never being called in the After example.

    opened by adampash 0
Releases(v0.3.1)
Owner
Alexandr Subbotin
Alexandr Subbotin
RxJS middleware for action side effects in Redux using "Epics"

RxJS-based middleware for Redux. Compose and cancel async actions to create side effects and more. https://redux-observable.js.org Install This has pe

redux-observable 7.8k Jan 6, 2023
Analytics middleware for Redux

redux-analytics Analytics middleware for Redux. $ npm install --save redux-analytics Want to customise your metadata further? Check out redux-tap. Usa

Mark Dalgleish 490 Aug 5, 2022
A mock store for testing Redux async action creators and middleware.

redux-mock-store A mock store for testing Redux async action creators and middleware. The mock store will create an array of dispatched actions which

Redux 2.5k Jan 1, 2023
In this course you learn how to use Redux as a state manager in your React applications

Redux is a state manager which you can use it in your React, Vue and even Vanilla applications. In this course we store Redux storage in localstorage to keep our data.

AmirHossein Mohammadi 5 Jul 25, 2022
A Higher Order Component using react-redux to keep form state in a Redux store

redux-form You build great forms, but do you know HOW users use your forms? Find out with Form Nerd! Professional analytics from the creator of Redux

Redux Form 12.6k Jan 3, 2023
redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.js state.

redux-immutable redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.js state. When Redux creat

Gajus Kuizinas 1.9k Dec 30, 2022
A lightweight state management library for react inspired by redux and react-redux

A lightweight state management library for react inspired by redux and react-redux

null 2 Sep 9, 2022
:recycle: higher order reducer to add undo/redo functionality to redux state containers

redux undo/redo simple undo/redo functionality for redux state containers Protip: Check out the todos-with-undo example or the redux-undo-boilerplate

Daniel Bugl 2.8k Jan 1, 2023
Redux Tutorial - share my experience regarding redux, react-redux and redux-toolkit

Redux Tutorial 1. Introduction to Redux 1.1 What is Redux & why Redux? A small JS Library for managing medium/large amount of states globally in your

Anisul Islam 36 Dec 29, 2022
A light-weight state manager.

Rectx a light-weight state manager with mutable api. 安装 npm install --save rectx 又一个轮子? Redux 和 Mobx 都非常的棒,但对于大部分项目都只是 CRUD 的项目来说,这些个玩意儿都略显太重了。而且对于 re

ZhengFang 177 Nov 17, 2022
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 3, 2023
Dead simple state management for React

Undux & TypeScript TodoMVC Example TodoMVC for Undux Installation git clone [email protected]:bcherny/undux-todomvc.git cd undux-todomvc npm install npm

Boris Cherny 11 Aug 10, 2020
Manage the state of your React app with ease

@spyna/react-store React app state management that uses a storage Demo https://spyna.github.io/react-store/ Install npm install --save @spyna/react-st

Lorenzo Spinelli 46 Jan 19, 2021
🪢 World's Simplest React State Manager

?? resso World's Simplest React State Manager (React 18, React Native, SSR, Mini Apps) Reactive shared store of React. No more extra re-render English

南小北 305 Dec 30, 2022
Skeleton React App configured with Redux store along with redux-thunk, redux persist and form validation using formik and yup

Getting Started with React-Redux App Some Configrations Needed You guys need to modify the baseUrl (path to your server) in the server.js file that is

Usama Sarfraz 11 Jul 10, 2022
This is a simple react project that Stores the books, When the user enter any book with the category will be updated in the API and you can get someof books from API based on the category. 📚

Book Store React ?? This is a simple react project that Stores the books, When the user enter any book with the category will be updated in the API an

Reem janina 9 Nov 10, 2022
A chart monitor for Redux DevTools https://www.npmjs.com/package/redux-devtools-chart-monitor

Redux DevTools Chart Monitor This package was merged into redux-devtools monorepo. Please refer to that repository for the latest updates, issues and

Redux 293 Nov 13, 2022
A simple app for study react with redux, redux saga and typescript.

React com Redux, Redux-Saga e TypeScript. ?? Uma aplicação simple para entender o funcionamento do Redux e a melhor maneira de utiliza-lo junto com o

João Marcos Belanga 1 May 24, 2022
Redux - Create forms using Redux And React

Exercício de fixação Vamos criar formulários utilizando Redux! \o/ Antes de inic

Márcio Júnior 2 Jul 21, 2022