Redux-persist: Cannot read property 'getState' of undefined.

Created on 5 Jun 2018  路  7Comments  路  Source: rt2zz/redux-persist

I am not finding any information via google search that helps. I thought I followed the directions correctly but something is wrong. I constantly get this error after logging into my app.

One thing I notice is that when I have my React Native Debugger open and viewing the redux state and actions It follows this sequence before showing the red screen of death.

@@INIT
persist/PERSIST
persist/REHYDRATE
AUTHENTICATED (my redux action that sets on correct login)
SET_SELECTED_CHAT (null value so no change when looking at diff)
SET_SELECTED_TAB (value assigned same as default so no change when looking at diff)
SET_IS_FETCHING_ASSIGNED_CHATS (value changes from false to true)
persists/REHYDRATE

ERROR appears and app stops running.

What really weirds me out is that the agent state is set in Redux and when I use AsyncStorage.getAllKeys() I can see the key in there. Also if I close the app and go back then it auto logs in... so I know local storage to redux is working.

Why this all fails on Chats.js is what has me super confused. Ie does redux-persist not handle multiple dispatches from a single action creator maybe??

"react": "16.3.1",
"react-native": "0.55.4",
"react-redux": "^5.0.6",
"redux-persist": "^5.9.1",
"redux-thunk": "^2.2.0",
"redux": "^3.7.2",

App.js

import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { View, StyleSheet } from 'react-native';

import { PersistGate } from 'redux-persist/integration/react';

import { store, persistor } from "../common/services/store";

import PrimaryNav from './router';

export default class App extends Component {
    render() {
        return (
            <Provider store={store}>
                <PersistGate loading={null} persistor={persistor}>
                    <View style={styles.app}>
                        <PrimaryNav />
                    </View>
                </PersistGate>
            </Provider>
        );
    }
}

store.js

import { applyMiddleware, compose, createStore } from "redux";
import thunk from "redux-thunk";
import reducers from '../reducers/rootReducer';

import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // defaults to localStorage for web and AsyncStorage for react-native

const persistConfig = {
    key: 'root',
    storage
};

const persistedReducer = persistReducer(persistConfig, reducers);

export const store = createStore(
    persistedReducer,
    compose(
        applyMiddleware(thunk),
        process.env.NODE_ENV === 'development' && window.devToolsExtension ? window.devToolsExtension() : f => f
    )
);

export const persistor = persistStore(store);

chatReducer.js (We use immutableJS)

const defaultState = {
    byId: {},
    assigned: {
        ids: [],
        totalRecords: 0,
        totalPages: 0,
        isFetching: false
    }
};

export const chatsReducer = (state = defaultState, action) => {
    switch(action.type) {
        case SET_VIEWED_CHAT:
            return update(state, {
                byId: {$merge: action.byId},
            });
        case SET_IS_FETCHING_ASSIGNED_CHATS:
            return update(state, {
                assigned: {
                    isFetching: {$set: action.isFetching}
                }
            });
        case SET_ASSIGNED_CHATS:
            return update(state, {
                byId: {$merge: action.byId},
                assigned: {
                    ids: {$union: action.ids},
                    totalPages: {$set: action.totalPages},
                    totalRecords: {$set: action.totalRecords},
                    isFetching: {$set: false}
                }
            });
        default:
            return state;
    }
};

chatAction.js

export const getChatsAction = (category = chatCategories.assigned, page = 1, limit = CHAT_REQUEST_LIMIT) => {
    let endpoint = '/chats';
    let queryString = `?page=${page}&per_page=${limit}&` + corsDebug();

    return (dispatch) => {
        dispatch({
            type: 'SET_IS_FETCHING_ASSIGNED_CHATS',
            isFetching: true
        });

        return axios.get(`${API_ROOT_URL}${endpoint}${queryString}`, setupAjaxHeaders())
            .then((response) => {
                const normalizedData = normalizeData(response.data);

                if (!isEmpty(normalizedData.entities)) {
                    dispatch({
                        type: 'SET_' + categoryConstant,
                        byId: normalizedData.entities.chat,
                        ids: normalizedData.result,
                        totalPages: parseInt(response.headers['x-pagination-pages'], 10),
                        totalRecords: parseInt(response.headers['x-pagination-total'], 10)
                    });
                } else {
                    dispatch({
                        type: 'SET_IS_FETCHING_ASSIGNED_CHATS',
                        isFetching: false
                    });
                }
            })
            .catch((error) => {
                dispatch({
                    type: 'SET_IS_FETCHING_ASSIGNED_CHATS',
                    isFetching: false
                });

                errorHandler(error);
            });
    };
};

Chats.js (page that gets called after successful login)

export class Chats extends Component {
    componentDidMount() {
        const { navigation } = this.props;
        const category = navigation.getParam('category', chatCategories.unassigned);

        this.props.setSelectedChatAction(null);
        this.props.setSelectedTabAction(category);
        this.props.getChatsAction(category); // Call the required action to get chat list data
   }

   render() {
        const { chats, pollForChatsAction } = this.props;

        return (
            <View style={styles.container}>
                <FlatList
                    data={chats.entities}
                    extraData={this.state}
                    renderItem={(item) => renderChatItem(item, this.onClickCallback)}
                    keyExtractor={(item, index) => index.toString() } // set the key for each list item
                    onEndReached={this.loadMore}
                    onEndReachedThreshold={0.1}
                    ListEmptyComponent={<NoChats/>}
                    ListFooterComponent={<LoadingIndicator isFetching={chats.isFetching}/>}
                />
            </View>
        )
    }
}

const mapStateToProps = (state) => ({
    category: state.uiContext.tabSelected,
    chats: getVisibleChats(state)
});

export default connect(mapStateToProps, {
    getChatsAction,
    setSelectedChatAction,
    setSelectedTabAction
})(Chats);
bug report

Most helpful comment

I do not see in your code you calling getState so not sure if this is same thing, but hit this as well. The issue in my case was an improper import.

Before redux-persist export default createStore(rootReducer, applyMiddleware(thunk)); so you could 'import store from ./src/store.js'

But if you follow the examples in this repo you need to import {store} from './src/store.js because you have no default export if you have the supplied sample code of:
export const store = createStore(pReducer);
export const storePersisted = persistStore(store);

All 7 comments

I'm also encountering this problem. Did you manage to resolve this?

I do not see in your code you calling getState so not sure if this is same thing, but hit this as well. The issue in my case was an improper import.

Before redux-persist export default createStore(rootReducer, applyMiddleware(thunk)); so you could 'import store from ./src/store.js'

But if you follow the examples in this repo you need to import {store} from './src/store.js because you have no default export if you have the supplied sample code of:
export const store = createStore(pReducer);
export const storePersisted = persistStore(store);

I'm also encountering this problem. Did you manage to resolve this?

No. I gave up on the library.

+1. Logging out Object.keys(store) returns dispatch,subscribe,getState,replaceReducer

Not sure what to do here

+1

I'm using redux with react-redux and facing the same issue here. exported the store and used it a component. it works when I assign a store.getState() to a value inside a function. calling store.getState() outside a function gives undefined. I think this might help with getting a solution
*Works here

  const getDataSource = () => {
      let emptyDataSource = []
      const storeState = store.getState()
      const cartItemsForCurrentUser = storeState.cart.filter(el => el.owner === getAuthUser().id)
       ...
}

*Does not work over here

   const storeState = store.getState() 
   console.log(storeState)

I got the same issue and solved by creating store on the same file as the root component where Provider is applied

Was this page helpful?
0 / 5 - 0 ratings