I create a React Native app. My index.ios.js where I add middlewares to the store.
import React, { Component } from 'react'
import {
AppRegistry,
} from 'react-native'
import { Provider } from 'react-redux'
import { applyMiddleware, createStore } from 'redux'
import createLogger from 'redux-logger'
import createSagaMiddleware from 'redux-saga'
import reducer from './src/reducers'
import * as sagas from './src/sagas'
import Scene from './src/components/scene'
const store = createStore(
reducer,
applyMiddleware(createSagaMiddleware(...sagas), createLogger())
);
const CitiesApp = () => (
<Provider store={store}>
<Scene />
</Provider>
);
AppRegistry.registerComponent('CitiesApp', () => CitiesApp);
I have an action:
export const USER_FETCH_REQUESTED = 'USER_FETCH_REQUESTED';
export const fetchUser = () => ({
type: USER_FETCH_REQUESTED
});
Which I call from componentDidMount in a component
componentDidMount() {
this.props.dispatch(fetchUser());
console.log('componentDidMount')
}
Also I have a saga:
export function* watchUserFetch() {
console.log('watchUserFetch');
yield take(actions.USER_FETCH_REQUESTED);
yield fork(fetchUser);
}
function* fetchUser() {
try {
const user = yield call(() => Api.fetchUser());
yield put({type: actions.USER_FETCH_SUCCEEDED, user});
} catch (e) {
yield put({type: actions.USER_FETCH_FAILED, message: e.message});
}
}
But the saga doesn't work. I don't saw watchUserFetch in console and fetchUser function doesn't run.
Where is my mistake? Why fetchUser doesn't run?
create root saga
export default function* rootSaga() {
yield [
watchUserFetch()
]
}
then run rootSaga
import createSagaMiddleware from 'redux-saga';
import rootSaga from './sagas.js';
const sagaMiddle= createSagaMiddleware();
const store = createStore(reducer,
applyMiddleware(sagaMiddle,createLogger())
);
sagaMiddle.run(rootSaga);
webpackbin example
Related
My Expo React Native project is continuously crashing on real device after building.
I used redux saga and its other dependencies for state management and for api call. I used documentation for to learn about. I dont have much knowledge about it.
App.js file
import { SafeAreaView } from "react-native";
import { NativeBaseProvider } from "native-base";
import { InsideStack } from "./src/stackNavigator";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import { store, persistor } from "./src/store";
const config = {
strictMode: "warn",
};
function App() {
return (
<SafeAreaView style={{ flex: 1 }}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<NativeBaseProvider config={config}>
<InsideStack />
</NativeBaseProvider>
</PersistGate>
</Provider>
</SafeAreaView>
);
}
export default App;
Redux index file
import { applyMiddleware } from "redux";
import { legacy_createStore as createStore } from "redux";
import createSagaMiddleware from "redux-saga";
import { persistStore, persistReducer } from "redux-persist";
import AsyncStorage from "#react-native-async-storage/async-storage";
import rootSagas from "./RootSagas";
import { rootReducer } from "./RootReducer";
const sagaMiddleware = createSagaMiddleware();
const persistConfig = {
key: "root",
storage: AsyncStorage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer, applyMiddleware(sagaMiddleware));
let persistor = persistStore(store);
sagaMiddleware.run(rootSagas);
export { store, persistor };
root reducer
import { combineReducers } from "#reduxjs/toolkit";
import MainReducer from "../screens/redux/reducers";
export const rootReducer = combineReducers({
mainreducer: MainReducer,
});
Root saga
import { takeEvery, all, fork, SelectEffect, select } from "redux-saga/effects";
import MainSagas from "../screens/redux/saga";
function* watchAndLog() {
yield takeEvery("*", function* logger(action) {
const state = yield select();
console.debug("action", action);
console.debug("state after", state);
});
}
export default function* root() {
console.log(process.env.NODE_ENV);
const allForks = [fork(MainSagas)];
if (process.env.NODE_ENV === "development") {
allForks.unshift(fork(watchAndLog));
}
yield all(allForks);
}
i want to cnnect redux-saga witdh react-native but this error keep happen...
TypeError: store.getState is not a function. (In 'store.getState()',
'store.getState' is undefined
Warning: Failed prop type: Invalid prop store of type function
supplied to Provider, expected object
this is my code
(index.js)
import {AppRegistry} from 'react-native';
import Root from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => Root);
(App.js)
import React from 'react';
import Store from './store/configureStore'
import {Provider} from 'react-redux';
import {App} from './src/index';
const Root = () => {
return (
<Provider store={Store}>
<App />
</Provider>
);
};
export default Root;
(src/index.js)
import React from 'react';
import Navigator from './Screens/Navigator';
import styled from 'styled-components/native';
const App = ({}) => {
return (
<Navigator/>
);
};
export {App};
(store/cofigurestore.js)
import { applyMiddleware, createStore, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from '../reducers';
import rootSaga from '../sagas';
const Store = () => {
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const enhancer = process.env.NODE_ENV === 'production'
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(
applyMiddleware(...middlewares),
);
const store = createStore(reducer, enhancer);
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
};
export default Store;
(reducer/index.js)
import { combineReducers } from 'redux';
import user from './user';
import post from './post';
// (이전상태, 액션) => 다음상태
const rootReducer = (state, action) => {
switch (action.type) {
// case HYDRATE:
// // console.log('HYDRATE', action);
// return action.payload;
default: {
const combinedReducer = combineReducers({
user,
post,
});
return combinedReducer(state, action);
}
}
};
export default rootReducer;
(sage/index.js)
import { all, fork } from 'redux-saga/effects';
import axios from 'axios';
import postSaga from './post';
import userSaga from './user';
export default function* rootSaga() {
yield all([
fork(postSaga),
fork(userSaga),
]);
}
please help me ......... i want to resolve this problem...... but i don't know how can i do that
In you App.js you should be passing the result of calling you Store function to the Provider and not the function itself.
const Root = () => {
return (
<Provider store={Store()}>
<App />
</Provider>
);
};
I am using saga.js with Redux in my project and I am trying to call an API but that API is not being called. The generator function is called, but with yield.put() other method is not being called. I am fairly new to Redux Saga and I am stuck here. Any help would be really appreciated.
Saga.js
import { put, takeEvery, all ,fork, takeLatest} from "redux-saga/effects";
import axios from "axios";
function* runOurAction() {
let remoteData;
yield axios.get(url).then((resp) => {
remoteData = resp.data;
});
yield put({ type: "SET_DATA", payload: remoteData });
};
function* getAsyncDataWatcher() {
yield takeLatest("GET_TEAMS", runOurAction);
}
export default function* rootSaga() {
yield fork(getAsyncDataWatcher)
}
getAsyncDataWatcher() is being called but its not calling runOurAction
Reducer.js
const teams=(state=[],action)=>{
switch(action.type) {
case "SAVE_TEAMS":
return { ...state, payload: action.payload };
case "GET_TEAMS":
return { ...state, payload: action.payload };
case "SET_DATA":
return { ...state, payload: action.payload };
default:
return state;
}
}
export default teams;
Actions.js
const getTeams = (payload) => {
return {
type: "GET_TEAMS",
payload:payload
};
};
const saveTeams = (payload) => {
return {
type: "SAVE_TEAMS",
payload:payload
};
};
export { saveTeams, getTeams };
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import rootReducer from "./reducers";
import createSagaMiddleware from "redux-saga";
import { createStore, applyMiddleware ,compose} from "redux";
import { Provider } from "react-redux";
import rootSaga from "./saga";
const sagaMiddleware = createSagaMiddleware();
const enhancers = [window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), applyMiddleware(sagaMiddleware)];
const store = createStore(
rootReducer,
compose(...enhancers)
);
sagaMiddleware.run(rootSaga);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
//
serviceWorker.unregister();
Team.js
import React,{useEffect} from 'react'
import {useSelector,useDispatch} from "react-redux";
import {getTeams} from "../actions";
const Team=()=> {
const data=useSelector(state=>state.teams);
const dispatch=useDispatch();
useEffect(() => {
console.log("Called");
dispatch(getTeams());
}, [dispatch])
console.log("hello",data);
return (
<div>
</div>
)
}
export default Team
The problem is probably caused by axios.get() as the return is a promise. Try the following:
yield axios.get(url).then((resp) => {
return resp.data;
}).then(response => {
remoteData = response;
});
I think you can't simply call axios function directly that way. You have to wrap it in call a saga-effects which only takes a function as its argument not a Promise resolve, so it would look like:
import { call } from 'redux-saga/effects';
// Wrap in a call which takes a function as argument
const remoteData = yield call(() => axios.get(url).then(response => repsonse.data));
// Or you can simply write in shorter way
const { data: remoteData } = yield call(axios.get, url);
I am trying to use redux-saga to call my API and passing it a parameter. I am using redux-logger and console.log() to debug. But it seems that my saga doesn't go call the API for some reason.
Here is the console log of the states... as you can see the apiMsg doesnt change, however I have it change for each action.
I think I am going wrong somewhere in the actions, reducer or the component. Or else I am calling the API wrong in the Saga.
This is my code
api.js
import axios from 'axios';
export function getPostApi(postId){
return axios.get(
`https://jsonplaceholder.typicode.com/posts/${postId}`
).then(
response => {
return response;
}
).catch(
err => {throw err;}
);
}
my store
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga'
import {createLogger} from 'redux-logger';
import rootReducer from '../Reducers';
export default function configureStore(initialState) {
const sagaMiddleware = createSagaMiddleware();
const logger = createLogger();
return {
...createStore(rootReducer, initialState, applyMiddleware(sagaMiddleware, logger)),
runSaga: sagaMiddleware.run
}
}
the saga
import {call, put, takeEvery} from 'redux-saga/effects';
import * as service from '../Services/api';
import * as actions from '../Actions';
import * as types from '../actions/actionTypes';
export function* fetchPost(action){
try{
yield put(actions.apiRequest());
const [post] = yield [call(service.getPostApi, action.postId)];
yield put(actions.apiRequestSucceeded(post));
} catch(e){
yield put(actions.apiRequestFailed());
}
}
export function* watchApiRequest() {
yield takeEvery(types.FETCH_POST, fetchPost);
}
actions
import * as types from './actionTypes';
export const fetchPost = (postId) => ({
type: types.FETCH_POST,
postId,
});
export const apiRequest = () => {
return {
type: types.API_REQUEST
}
}
export const apiRequestSucceeded = (post) => {
return {
type: types.API_REQUEST_SUCCEEDED,
post
}
}
export const apiRequestFailed = () => {
return {
type: types.API_REQUEST_FAILED
}
}
reducer
import * as types from '../actions/actionTypes';
const initialState = {
apiMsg: '',
postId: 1,
fetching: false,
post: null
};
export default function getPostData(state = initialState, action) {
switch (action.type) {
case types.API_REQUEST:
return {
...state,
apiMsg: 'API call request!',
fetching: true
}
case types.API_REQUEST_SUCCEEDED:
return {
...state,
apiMsg: 'API called succeeded!',
fetching: false,
postId: action.post.id,
post: action.title
};
case types.API_REQUEST_FAILED:
return {
...state,
apiMsg: 'API called failed!',
fetching: false,
};
default:
return state;
}
}
the component page
import React, { Component } from 'react';
import { View, Text} from 'react-native';
import { connect } from 'react-redux';
import {fetchPost} from './Actions/apiTesterActions';
class TestPage extends Component {
constructor() {
super();
}
componentDidMount() {
this.props.fetchPost(1)
}
render() {
const { postId, fetching, post } = this.props;
console.log('Test page props', postId, fetching, post);
return (
<View>
<Text>Test Page Works! </Text>
</View>
)
}
}
const mapStateToProps = (state) => {
return {
postId: state.getPostData.postId,
fetching: state.getPostData.fetching,
post: state.getPostData.post
}
};
export default connect(mapStateToProps, {fetchPost})(TestPage);
I am still learning Redux, and now redux-saga, so I don't fully understand the structure/hierarchy, but I am learning and any advice is helpful. Thank you
EDIT:
I am using this redux saga api call example as a guide.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import configureStore from './ReduxTest/Store/configureStore';
import rootSaga from './ReduxTest/sagas';
const store = configureStore();
store.runSaga(rootSaga)
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
registerServiceWorker();
saga/index.js
import {fork, all} from 'redux-saga/effects';
import {watchApiRequest} from './apiTester';
export default function* rootSaga() {
yield all [
fork(watchApiRequest)
]
}
After some time with a lot of debugging, I have figured the problem.
When I first started creating the application, I got the following error...
[...effects] has been deprecated in favor of all([...effects]), please update your code
As the yield all [...] was deprecated, I changed my code without taking it into consideration, I was happy the error went, but didn't realize how much of an impact it would have on my application.
(wrong) saga/index.js
import {fork, all} from 'redux-saga/effects';
import {watchApiRequest} from './apiTester';
export default function* rootSaga() {
yield all [
fork(watchApiRequest)
]
}
(right) saga/index.js
import {fork, all} from 'redux-saga/effects';
import {watchApiRequest} from './apiTester';
export default function* rootSaga() {
yield all ([
fork(watchApiRequest)
])
}
As You can see it needed to be wrapped in brackets yield all ([...])
You will get the deprecated error but your code will still work. I am unsure how to fix the deprecated error.
If someone knows how to get rid of the deprecated error than that would great.
I am new to the saga world. Although I have worked with thunk on react-native territory, I am very confused at the moment. I am trying to get the skeleton of my project going which I expect to get very large soon. With that in mind, I am trying to separate the logic into multiple files.
I have gotten the reducer to fire except it is not the way I want. I am not sure how it is even happening. My saga does not fire but my state updates. I see the console log from my reducer but nothing from the saga watcher function. What should I change?
Index.js
import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { Provider } from 'react-redux'
import reducer from './reducers'
import rootSaga from './sagas'
import App from './App'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("etlRootDiv"),
);
App.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import FileExtensionSelector from './components/FileExtensionSelector'
import { setFileExtension } from './actions'
class App extends Component {
constructor(props) {
super(props)
}
handleTypeSelect() {
console.log('handle more click');
this.props.setFileExtension('zip');
console.log(this.props);
}
componentWillReceiveProps(nextProps){
console.log(nextProps);
}
render() {
return (
<div>
<FileExtensionSelector onFileTypeSelect={this.handleTypeSelect.bind(this)} />
<div>{this.props.fileType} ...asdasd</div>
</div>
)
}
}
const mapStateToProps = ({ metaState }) => {
const { fileType } = metaState;
return { fileType };
};
const mapDispatchToProps = (dispatch) => ({
setFileExtension(ext) {
dispatch(setFileExtension(ext))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(App)
reducers/index.js
import { combineReducers } from 'redux';
import metaState from './MetaStateReducer';
const rootReducer = combineReducers({
metaState,
})
export default rootReducer
reducers/metastatereducer.js
const INITIAL_STATE = {
fileType: null,
hasHeader: false,
};
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case 'SET_FILE_EXTENSION':
console.log('/// in set file reducer ///');
console.log(action);
// console.log({ ...state, ...INITIAL_STATE, fileType: action.payload });
return { ...state,...INITIAL_STATE, fileType: action.payload };
default:
return state;
}
}
actions/metaStateActions.js
function action(type, payload = {}) {
return { type, ...payload }
}
export const SET_FILE_EXTENSION = "SET_FILE_EXTENSION";
export const setFileExtension = (extension) => action( SET_FILE_EXTENSION, { payload: extension });
actions/index.js
export { setFileExtension, SET_FILE_EXTENSION } from './metaDataActions';
sagas/metastatesagas.js
import { take, put } from 'redux-saga/effects'
import { SET_FILE_EXTENSION } from '../actions';
function* watchFileExtension(ext) {
console.log(' --- in watch file ext ---');
const { extension } = yield take(SET_FILE_EXTENSION)
console.log(`set extension is ${extension}`);
// yield put({ type: 'SET_FILE_EXTENSION', payload: ext });
}
export const metaStateSagas = [
take("SET_FILE_EXTENSION", watchFileExtension),
]
sagas/index
import { all } from 'redux-saga/effects'
import { metaStateSagas } from './MetaStateSagas';
export default function* rootSaga() {
yield all([
...metaStateSagas,
])
}
redux-saga always passes an action along to the store before attempting to process itself. So, the reducers will always run before any saga behavior executes.
I think the error is that your metaStateSagas array needs to use takeEvery, not take, but I'm not entirely sure. Try that and see if it fixes things.