redux-persist icon indicating copy to clipboard operation
redux-persist copied to clipboard

Cannot read property 'getState' of undefined.

Open thorakmedichi opened this issue 7 years ago • 14 comments

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);

thorakmedichi avatar Jun 05 '18 18:06 thorakmedichi

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

emilany avatar Sep 07 '18 01:09 emilany

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);

askpatrickw avatar Oct 15 '18 19:10 askpatrickw

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

No. I gave up on the library.

thorakmedichi avatar Oct 19 '18 16:10 thorakmedichi

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

Not sure what to do here

dillonfromdallas avatar Nov 15 '18 15:11 dillonfromdallas

+1

code-freakz avatar Dec 17 '18 08:12 code-freakz

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

akhilthankachen avatar Feb 07 '20 14:02 akhilthankachen

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

This worked for me aswell, thanks!

leGenti avatar Jun 02 '21 14:06 leGenti

In my case I imported store default export as import Store from './redux/store Then used the store as Store.store and persistor as Store.persistor Cheers!

wevak avatar Jul 27 '21 13:07 wevak

Just in case checkout this one too - https://stackoverflow.com/questions/51509298/cannot-read-property-subscribe-of-undefined-redux-persist/51519967#51519967

wevak avatar Jul 27 '21 13:07 wevak

don't export default to store, both store or persister export only, problem wiil be solve.. this is my store code in Next.js and it's worked for me.

import { createStore, combineReducers, applyMiddleware, compose } from 'redux'; import { FREE_TRAIL_SUBJECT_DATA } from '@utils/free_trial_subject_data'; import updateUserData from '@redux/Reducers/UserDataReducer'; import updateUserPurchaseChapter from '@redux/Reducers/UserPurchaseChapterReducer'; import updateUserCourseMood from '@redux/Reducers/UserCourseMoodReducer'; import updateQuestionsData from '@redux/Reducers/questionReducer'; import basketReducer from '@redux/Reducers/BasketReducer'; import thunk from 'redux-thunk'; import { persistStore,persistReducer } from 'redux-persist'; import storage from 'redux-persist/lib/storage';

const persitConfig={ key:'persist-store', storage } const allReducer = combineReducers({ userData: updateUserData, userPurchaseChapter: updateUserPurchaseChapter, userCourseMood: updateUserCourseMood, questions: updateQuestionsData, basket: basketReducer, });

const initialStates = { userData: null, userPurchaseChapter: FREE_TRAIL_SUBJECT_DATA, userCourseMood: null, questions: null, basket: null, }; const persistedReducer = persistReducer(persitConfig,allReducer) const middleware = [thunk];

const enhancers = compose( applyMiddleware(...middleware), typeof window !== 'undefined' && window.devToolsExtension ? window.REDUX_DEVTOOLS_EXTENSION && window.REDUX_DEVTOOLS_EXTENSION() : (value) => value );

export const store = createStore(persistedReducer, initialStates, enhancers); export const persister=persistStore(store);

choudhary-ankit avatar Nov 10 '21 03:11 choudhary-ankit

I had the following store:

import {configureStore} from '@reduxjs/toolkit';
import userReducer from './userSlice';

export default configureStore({
  reducer: {
    user: userReducer,
  }
});

My problem was that I was importing a const that didn't exist import { store } from "./store";. The thing was that ./store is exporting the store by default

I solved it just by importing the default object that the file is exporting.

import store from './app/store';
import { Provider } from 'react-redux';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

I don't need to configure the store in the same file where the provider is applied.

geovanygameros avatar Mar 27 '22 17:03 geovanygameros

@choudhary-ankit

yes removing the export did solve my issue as well , Thanks!

erAnusriM avatar May 16 '22 04:05 erAnusriM

This worked for me. Any recommendations how can I achieve code splitting though. My app.js is getting huge with this.

micScofield avatar Dec 16 '22 18:12 micScofield

he thing was that ./store is exporting the store by default

@geovanygameros Yes, i made the same import mistake and now it's fixed

Gupta-Sanskriti avatar Feb 12 '23 09:02 Gupta-Sanskriti

i had not given store prop at all

kishanhirani avatar Mar 16 '24 07:03 kishanhirani