Redux-saga: Error on configuring the store - react-native

I am trying to create a store and apply redux-saga middleware to it. I have configured every thing, But when I run the project, the following error pops up.
***Error: Before running a saga, you must mount the saga middleware on the store
using applyMiddleware
I error ocures on line sagaMiddleware.run(sagas);.
store.js
import { createStore, applyMiddleware, compose } from 'redux';
import { createLogger } from 'redux-logger';
import createSagaMiddleware from 'redux-saga';
import sagas from '../redux/sagas';
const logger = createLogger({
predicate: (getState, action) => isDebuggingInChrome,
collapsed: true,
duration: true,
diff: true,
});
export default function configureStore() {
const sagaMiddleware = createSagaMiddleware();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
applyMiddleware(sagaMiddleware, logger),
);
sagaMiddleware.run(sagas);
return store;
}
Any Idea what am I doing wrong?
react-native: 0.57.0
redux-saga: ^0.16.0
redux: ^4.0.0

That's because you are not creating the store correctly, notice that you are not passing the rootReducer?
createStore is an enhancer that returns a function that takes the rootReducer as a parameter and the result of that is the store.
You probably want to do something like this, instead:
import { createStore, applyMiddleware, compose } from 'redux';
import { createLogger } from 'redux-logger';
import createSagaMiddleware from 'redux-saga';
import sagas from '../redux/sagas';
import rootReducer from '../redux/rootReducer';
const logger = createLogger({
predicate: (getState, action) => isDebuggingInChrome,
collapsed: true,
duration: true,
diff: true,
});
const enhancers = process.env.NODE_ENV !== 'production' && window.devToolsExtension
? [window.devToolsExtension()]
: [];
export default function configureStore() {
const sagaMiddleware = createSagaMiddleware();
const store = compose(
applyMiddleware(sagaMiddleware, logger),
...enhancers
)(createStore)(rootReducer);
sagaMiddleware.run(sagas);
return store;
}

Related

How to persist redux data in react native

I’ve been struggling on this problem for while n now it’s a pretty simple issue I need a way to persist my data from redux into react native’s local storage I have tried using multiple approaches like the redux-persist library but I still have not been able to crack it… has anyone found a solution to this issue, I’m open to all approaches.
Here is the code I used for the redux-persist library
store.ts
import {createStore, combineReducers, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {persistStore} from 'redux-persist';
import authReducer from './reducers/authReducer';
import toastReducer from './reducers/toastReducer';
const rootReducer = combineReducers({
toast: toastReducer,
auth: authReducer,
});
export const Store = createStore(rootReducer, applyMiddleware(thunk));
export const PersistedStore = persistStore(Store);
export type RootState = ReturnType<typeof Store.getState>;
export type AppDispatch = typeof Store.dispatch;
authReducer.ts
import AsyncStorage from '#react-native-async-storage/async-storage';
import {persistReducer} from 'redux-persist';
import {AuthResponse} from '../../types/AuthResponse';
import {AuthReduxAction} from '../../types/ReduxActions';
import {SET_USER_DETAILS} from '../constants';
const persistConfig = {
key: 'root',
storage: AsyncStorage,
};
interface State {
userDetails: AuthResponse;
}
const initialState: State = {
userDetails: {},
};
const authReducer: (
state: State,
action: AuthReduxAction,
) => State | Promise<State> = async (state = initialState, action) => {
switch (action.type) {
case SET_USER_DETAILS:
// Saving user details to local storage
return {...state, userDetails: action.payload};
default:
return state;
}
};
export default persistReducer(persistConfig, authReducer);
App.tsx
import React from 'react';
import Routes from './Routes';
import {Provider} from 'react-redux';
import {PersistGate} from 'redux-persist/integration/react';
import {LogBox} from 'react-native';
import {PersistedStore, Store} from './redux/store';
LogBox.ignoreLogs(['new NativeEventEmitter']);
const App = () => {
return (
<Provider store={Store}>
<PersistGate loading={null} persistor={PersistedStore}>
<Routes />
</PersistGate>
</Provider>
);
};
export default App;
Here is the response I get when I print out the data (auth data in this case) from the redux store:
{"_U": 0, "_V": 0, "_W": null, "_X": null, "_persist": {"rehydrated": true, "version": -1}}
Any help would be appreciated
Thanks
I would like to do redux-persist this way and it works for several projects. Check it out if it works for you.
store.js
import { applyMiddleware, createStore, compose } from "redux";
import { persistStore, persistReducer } from "redux-persist";
import AsyncStorage from "#react-native-async-storage/async-storage";
import thunkMiddleware from "redux-thunk";
import { createLogger } from "redux-logger";
import reducer from "./reducers/";
const enhancers = [
applyMiddleware(
thunkMiddleware,
createLogger({
collapsed: true,
// eslint-disable-next-line no-undef
predicate: () => __DEV__,
})
),
];
/* eslint-disable no-undef */
const composeEnhancers =
(__DEV__ &&
typeof window !== "undefined" &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
/* eslint-enable no-undef */
const enhancer = composeEnhancers(...enhancers);
const persistConfig = {
key: "root",
storage: AsyncStorage,
blacklist: [],
};
const persistedReducer = persistReducer(persistConfig, reducer);
export const store = createStore(persistedReducer, {}, enhancer);
export const persistor = persistStore(store);
authReducer.js
import {AuthResponse} from '../../types/AuthResponse';
import {AuthReduxAction} from '../../types/ReduxActions';
import {SET_USER_DETAILS} from '../constants';
interface State {
userDetails: AuthResponse;
}
const initialState: State = {
userDetails: {},
};
const authReducer: (
state: State,
action: AuthReduxAction,
) => State | Promise<State> = async (state = initialState, action) => {
switch (action.type) {
case SET_USER_DETAILS:
// Saving user details to local storage
return {...state, userDetails: action.payload};
default:
return state;
}
};
export default authReducer;
index.js (same location as authReducer.js)
import { combineReducers } from 'redux';
import authReducer from './authReducer';
import toastReducer from './toastReducer';
export default combineReducers({
authReducer ,
toastReducer
});

Navigate in action with redux, debug with reactotron error

I try to navigate inside actions of redux, but the command NavigationActions.navigate don't worked, so I installed reactotron to try debug with reactotron, but this cause the error of image, below are the code and de error. Some help me please.
The action
export function* searchByID(action) {
try {
console.log('action', action);
const response = yield call(apiGetProdutoByID, action.idProduto);
console.log('produto', response);
yield put({
type: TypesProdutos.SEARCH_BY_ID,
produto: response.data.data.obj,
});
const produto = yield select(state => state.produtos);
console.log(produto);
**yield put(
NavigationActions.navigate({
routeName: action.route,
params: {produto: produto},
}),**
);
} catch (error) {
console.log(error);
yield put({type: TypesProdutos.FAIL});
}
}
ReactotronConfig.js
import Reactotron from 'reactotron-react-native';
import AsyncStorage from '#react-native-community/async-storage';
import {reactotronRedux as reduxPlugin} from 'reactotron-redux';
import sagaPlugin from 'reactotron-redux-saga';
const reactotron = Reactotron;
Reactotron.setAsyncStorageHandler(AsyncStorage) // AsyncStorage would either come from `react-native` or `#react-native-community/async-storage` depending on where you get it from
.configure() // controls connection & communication settings
.useReactNative() // add all built-in react native plugins
.use(reduxPlugin())
.use(sagaPlugin())
.connect(); // let's connect!
export default reactotron;
index.js
if (__DEV__) {
import('./ReactotronConfig').then(() => console.log('Reactotron Configured'));
}
import {AppRegistry} from 'react-native';
import App from './src';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
index.js (store)
import {createStore, applyMiddleware} from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './ducks';
import rootSaga from './sagas';
import Reactotron from 'reactotron-react-native';
const sagaMonitor = Reactotron.createSagaMonitor;
const sagaMiddleware = createSagaMiddleware({sagaMonitor});
const middleware = applyMiddleware(sagaMiddleware);
const store = createStore(rootReducer, middleware);
sagaMiddleware.run(rootSaga);
export default store;
error:
error
After command npm install and make this modifications in the file where I export store, the app worked.
import {createStore, applyMiddleware, compose} from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './ducks';
import rootSaga from './sagas';
import Reactotron from 'reactotron-react-native';
import ReactotronConfig from '../../ReactotronConfig';
const sagaMonitor = Reactotron.createSagaMonitor;
const sagaMiddleware = createSagaMiddleware({sagaMonitor});
const middleware = applyMiddleware(sagaMiddleware);
const store = createStore(
rootReducer,
compose(
middleware,
ReactotronConfig.createEnhancer(),
),
);
sagaMiddleware.run(rootSaga);
export default store;

How to persist redux store in React Native with redux-persist

I am using react-native with redux-persist and I cannot seem to get my store to persist into async storage. According to the redux-persist github repo, this may have to do with AsyncStorage being removed from react-native, but I cannot seem to figure out what needs to be done to fix the issue.
When my app launches I see the actions persist/PERSIST and persist/REHYDRATE fire in my redux devtools, but nothing ever is stored in async storage.
I thought I had set up my file as the redux-persist docs explained but I imagine I am missing something. I'm fairly new to Redux as it is so I may just be confused on something basic.
My Redux.config.js file ...
import React from 'react';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux'
import thunk from 'redux-thunk';
import { initialState } from './initialState';
import { puppyReducer } from './reducers/puppies/puppyReducer';
import { uiReducer } from './reducers/ui/uiReducer';
import { userReducer } from './reducers/user/userReducer';
import { contentReducer } from './reducers/content/contentReducer';
import { persistStore, persistReducer, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/lib/storage'
import logger from 'redux-logger';
import Navigation from './Navigation.config';
import { PersistGate } from 'redux-persist/integration/react';
const persistConfig = {
key: 'root',
storage: storage
}
const rootReducer = {
dogs: puppyReducer,
ui: uiReducer,
user: userReducer,
content: contentReducer
};
const reducer = persistCombineReducers(persistConfig, rootReducer);
const persistedReducer = persistReducer(persistConfig, reducer);
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(persistedReducer, initialState, composeEnhancers(applyMiddleware(thunk), applyMiddleware(logger)));
const persistor = persistStore(store);
// Wrap the redux State Provider around the Main Navigation Stack
function StateWrapper() {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Navigation />
</PersistGate>
</Provider >
)
}
export default App = StateWrapper;
I expect that this should load my persisted state when the app launches, and continue to update that persisted state as my redux state changes. As it stands currently, my state is updating fine, but nothing is persisted. There are no error messages showing.
I persist my redux store using AsyncStorage. Rather than using redux-persist's storage option, I have installed AsyncStorage from the #react-native-community. Just make sure that you link the dependency properly.
Then in my config.js file I import and use AsyncStorage as follows.
import AsyncStorage from '#react-native-community/async-storage';
const config = {
key: 'primary',
keyPrefix: '', // the redux-persist default `persist:` doesn't work with some file systems
storage: AsyncStorage,
};
Your issue could also be related to the keyPrefix try setting it to some value other than the default.
Try the following:
const reducer = persistCombineReducers(persistConfig, rootReducer);
const persistedReducer = persistReducer(persistConfig, reducer rootReducer);
This works for me:
//...
import {combineReducers} from 'redux';
import {persistReducer} from 'redux-persist';
const persistConfig = {
key: 'root',
storage: storage
}
const rootReducer = {
dogs: puppyReducer,
ui: uiReducer,
user: userReducer,
content: contentReducer
};
const persistedReducer = persistReducer(
persistConfig,
combineReducers(rootReducers)
);
//...

How to fix this error "combineReducers" are read-only. in react-native/react-redux

I'm creating a new todo app in react-native using redux.
On setting up the rootReducer using combineReducers i'm getting the following error.
"combineReducers" is read-only.
// reducers/todos.js
let nextId = 0;
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state, {
id: nextId++,
text: action.text,
completed: false,
}
]
case 'TOGGLE_TODO':
return state.map(todo =>
(todo.id === action.id) ? { ...todos, completed: !todo.completed } : todo)
default:
return state
}
}
export default todos;
created a visibility filter to toggle the todo state.
// reducers/visibilityFilter.js
const visibilityFilter = (state='SHOW_ALL', action) =>{
return state;
}
export default visibilityFilter;
combining the both reducers into index file.
// reducers/index.js
import { combineReducers } from 'redux';
import todos from './todos';
import visibilityFilter from './visibilityFilter';
export default combineReducers = combineReducers({
todos,
visibilityFilter
})
Here is my store file
import { createStore } from 'redux';
import rootReducer from '../reducers';
export default store = createStore(rootReducer);
As the message clearly states, you are not allowed to reassign combineReducers. Just change your code to the following, as described in the docs:
export default rootReducer = combineReducers({
todos,
visibilityFilter
})
Why do you have this like this?
export default combineReducers = combineReducers({
todos,
visibilityFilter
})
combineReducers() are written in this manner:
import { combineReducers } from "redux";
export default combineReducers({
todos: () => {
return {};
}
});
Give that a try and remember a reducer must always return an object, a string or a number, this is the minimum requirement for getting started with reducers in order to ensure your app doesn't break and that you can get some content to the screen.
Also, you don't necessarily need to identify your combineReducers() as rootReducer, you can just put it together like so:
import { createStore, compose, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducers from "../reducers";
const store = createStore(reducers, {}, compose(applyMiddleware(thunk)));
export default store;
Lastly, when you have created your store, you need to import it back to your App.js and wire it up with the Provider tag like so for example:
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import {
createBottomTabNavigator,
createStackNavigator,
createAppContainer
} from "react-navigation";
import { Provider } from "react-redux";
import store from "./store";
And:
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<View style={styles.container}>
<MainNavigator />
</View>
</Provider>
);
}
}

No reducer for key (react native)

I am trying to integrate redux navigation to my existing app. It shows me the error No reducer provided for key navReducer. Any help is appreciated. Other stackoverflow answers point to the fact that there is problem in importing when this kind of error occurs but I am not sure why this is occuring in my case.
My /components/navigation.js:
import React, { Component } from 'react'
import { createStackNavigator } from 'react-navigation'
import Login from '../containers/loginContainer'
import Home from '../containers/homeContainer'
import {
reduxifyNavigator,
createReactNavigationReduxMiddleware,
createNavigationReducer,
} from 'react-navigation-redux-helpers'
export const RootStack = createStackNavigator(
{
Login: {
screen: Login,
},
Home: {
screen: Home,
},
},
{
initialRouteName: 'Login'
}
)
export const navReducer = createNavigationReducer(RootStack)
export const navigationMiddleware = createReactNavigationReduxMiddleware(
"root",
state => state.nav,
)
const appNavigator = reduxifyNavigator(RootStack, "root")
const mapStateToProps = (state) => ({
state: state.nav,
})
export const AppWithNavigationState = connect(mapStateToProps)(appNavigator)
My store configuration is:
import { createStore, applyMiddleware, compose } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import thunk from 'redux-thunk'
import storage from 'redux-persist/lib/storage'
import rootReducer from './reducers'
import { navigationMiddleware } from './components/navigation'
const persistConfig = {
key: 'myKey',
storage,
}
const middlewares = [thunk, navigationMiddleware]
const persistedReducer = persistReducer(persistConfig, rootReducer)
const store = createStore(
persistedReducer,
{},
compose(
applyMiddleware(...middlewares)
)
)
const persistedStore = persistStore(store)
export default store
My root reducer looks something like this:
import { combineReducers } from 'redux';
import { navReducer } from '../components/navigation'
const rootReducer = combineReducers({
navReducer, // It throws the error here!!
})
export default rootReducer