rt2zz / redux-persist

persist and rehydrate a redux store
MIT License
12.95k stars 866 forks source link

Cannot read property 'getState' of undefined. #837

Open thorakmedichi opened 6 years ago

thorakmedichi commented 6 years ago

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);
emilany commented 6 years ago

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

askpatrickw commented 6 years ago

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

thorakmedichi commented 6 years ago

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

No. I gave up on the library.

dillonfromdallas commented 5 years ago

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

Not sure what to do here

code-freakz commented 5 years ago

+1

akhilthankachen commented 4 years ago

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

leGenti commented 3 years ago

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!

choudhary-ankit commented 2 years ago

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

geovanygameros commented 2 years ago

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.

erAnusriM commented 2 years ago

@choudhary-ankit

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

micScofield commented 1 year ago

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

Gupta-Sanskriti commented 1 year ago

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

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

kishanhirani commented 7 months ago

i had not given store prop at all