Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

saga twice calls #1985

Open
Deboracgs opened this issue Nov 24, 2019 · 23 comments
Open

saga twice calls #1985

Deboracgs opened this issue Nov 24, 2019 · 23 comments
Labels

Comments

@Deboracgs
Copy link

Deboracgs commented Nov 24, 2019

My saga is calling twice, already put take and takeLatest, but it didn't work.
The another sagas called normal.

export const postProductRequest = async (data) =>
     await API.post('product', data);
     
function* postProductAPI({ payload }) {
    try {
     
        let product = payload.data;
        const request = yield call(postProductRequest, product);
        yield put(postProductAPISuccess(request));
    } catch (error) {
        if(error.response != undefined){
            if(error.response.data.message != undefined){
                if(error.response.data.message.error != undefined){
                    yield put(postProductAPIFailure(error.response.data.message.error));
                } else{
                    yield put(postProductAPIFailure("internal_error"));
                }
            }else{
                yield put(postProductAPIFailure("internal_error"));
            }
            
        }else{
            yield put(postProductAPIFailure("internal_error"));
        }
    }
}

export function* postProduct() {
    yield takeEvery(POST_PRODUCT_API, postProductAPI);
}
@khaliu
Copy link

khaliu commented Dec 2, 2019

Hi there!
I don´t see anything weird in the code that you posted, as a side note I wouldn't repeat the same code in the catch block.

You can try tracing which part of the code is dispatching the POST_PRODUCT_API action with Redux Devtools. (You can find a trace tab in which you will see a calling stack of each dispatched action)

@Deboracgs
Copy link
Author

the action is called once, but in saga called twice, I not have any idea, I will shared here my actions @khaliu

@satp8
Copy link

satp8 commented Feb 11, 2020

i also have same issue

@khaliu
Copy link

khaliu commented Feb 12, 2020

If you want, you can share your code and setup in a codesandbox(https://codesandbox.io/) just the important part, to see if I can help

@Deboracgs
Copy link
Author

import { all, call, fork, put, takeLatest } from 'redux-saga/effects';

import {
    POST_PRODUCT_API
} from './../../actions/types';

import {
    postProductAPIFailure,
    postProductAPISuccess
} from './../../actions';

import {API} from './../../util/api';


export const imageNewUploadRequest = async (image) =>
    await API.post('image/upload', image, {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    });

export const postProductRequest = async (data) =>
     await API.post('product', data);
     
function* postProductAPI({ payload }) {
    try {
        console.log("entra aqui", payload.data)
        let product = payload.data;
        let file = payload.data.thumbnail;
        if(file != null){
            if(typeof(file[0]) === 'object'){
                const formData = new FormData();
                formData.append('thumbnail', file[0])
                const image = yield call(imageNewUploadRequest, formData);
                product.thumbnail = image.data
            }
        }

        console.log("entra aqui 2")
        

        const request = yield call(postProductRequest, product);
        console.log("entra aqui 3", request)
        yield put(postProductAPISuccess(request));
    } catch (error) {
        if(error.response != undefined){
            if(error.response.data.message != undefined){
                if(error.response.data.message.error != undefined){
                    yield put(postProductAPIFailure(error.response.data.message.error));
                } else{
                    yield put(postProductAPIFailure("internal_error"));
                }
            }else{
                yield put(postProductAPIFailure("internal_error"));
            }
            
        }else{
            yield put(postProductAPIFailure("internal_error"));
        }
    }
}

export function* postProduct() {
    yield takeLatest(POST_PRODUCT_API, postProductAPI);
}

export default function* rootSaga() {
    yield all([
        fork(postProduct)
    ]);
}

@Deboracgs
Copy link
Author

This is my code, in the actions called once, but when entry in postProductAPI function, called twice

@Deboracgs
Copy link
Author

I just did make an alternative solution (gambiarra in brazilian portuguese), with a counter... Does anybody have a better solution?

import { all, call, fork, put, takeLatest, takeEvery } from 'redux-saga/effects';

import {
    POST_PRODUCT_API
} from './../../actions/types';

import {
    postProductAPIFailure,
    postProductAPISuccess
} from './../../actions';

import {API} from './../../util/api';
var cont = 0;

export const imageNewUploadRequest = async (image) =>
    await API.post('image/upload', image, {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    });

export const postProductRequest = async (data) =>
     await API.post('product', data);
     
function* postProductAPI2({ payload }) {
    try {
        cont = cont + 1;
        if(cont == 1){
            let product = payload.data;
            let file = payload.data.thumbnail;
            if(file != null){
                if(typeof(file[0]) === 'object'){
                    const formData = new FormData();
                    formData.append('thumbnail', file[0])
                    const image = yield call(imageNewUploadRequest, formData);
                    product.thumbnail = image.data
                }
            }
    
            const request = yield call(postProductRequest, product);
            yield put(postProductAPISuccess(request));
            cont = 0;
        }
       
    } catch (error) {
        ont = 0;
        if(error.response != undefined){
            if(error.response.data.message != undefined){
                if(error.response.data.message.error != undefined){
                    yield put(postProductAPIFailure(error.response.data.message.error));
                } else{
                    yield put(postProductAPIFailure("internal_error"));
                }
            }else{
                yield put(postProductAPIFailure("internal_error"));
            }
            
        }else{
            yield put(postProductAPIFailure("internal_error"));
        }
    }
}

export function* postProduct() {
    yield takeLatest(POST_PRODUCT_API, postProductAPI2);
}

export default function* rootSaga() {
    yield fork(postProduct)
}

@z0al
Copy link

z0al commented Mar 10, 2020

I might be wrong, but if your sagas are being called twice while actions are only being fired once then it's probably a configuration issue (e.g. saga middleware injected twice into Redux) .

@Deboracgs can you share your store configs (i.e. the part where you inject sagaMiddleware into createStore)?

@Deboracgs
Copy link
Author

@z0al yes, this probably a issue, but this is weird, because only in this call this is happen, I created another function and this happen twice, I'll share with you later the store config .

@rafaelnunes22
Copy link

@Deboracgs I had the same issue and the @z0al is right, the problem is in yout store config.
"store.sagaTask = sagaMiddleware.run(sagas);
sagaMiddleware.run(sagas);"
in my case, sagaMiddleware.run(sagas) was been called twice.

@jgudo
Copy link

jgudo commented Jan 28, 2021

Had the same problem and fixed it with @z0al 's suggestion

My previous store configuration looked like this

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};

export default configureStore;

And I was using it both in my React index.js and api.js

import configureStore from '~/redux/store';
const store = configureStore();

Changed my default export to a single instance of my configureStore instead of exporting the function

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};
const store = configureStore() // exported this instead;

export default store;

@goldmont
Copy link

Have you enabled React.StrictMode in your index.js/ts?

@sudhiryadav
Copy link

Had the same problem and fixed it with @z0al 's suggestion

My previous store configuration looked like this

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};

export default configureStore;

And I was using it both in my React index.js and api.js

import configureStore from '~/redux/store';
const store = configureStore();

Changed my default export to a single instance of my configureStore instead of exporting the function

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};
const store = configureStore() // exported this instead;

export default store;

This fixed my issue of double calls. This was exactly what I was doing.

@hvlong
Copy link

hvlong commented Jun 27, 2021

Had the same problem and fixed it with @z0al 's suggestion

My previous store configuration looked like this

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};

export default configureStore;

And I was using it both in my React index.js and api.js

import configureStore from '~/redux/store';
const store = configureStore();

Changed my default export to a single instance of my configureStore instead of exporting the function

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};
const store = configureStore() // exported this instead;

export default store;

Many thanks! you saved me.

@amir-gorji
Copy link

I had the same problem when I transformed my "App.tsx" class Component into a Functional Component.
The problem was only with development build.
I put the sagaMiddleware.run(rootSaga); into a useEffect hook and the problem fixed.

useEffect(() => {
    sagaMiddleware.run(rootSaga);
  }, []);

@thevijaysoni
Copy link

Had the same problem and fixed it with @z0al 's suggestion

My previous store configuration looked like this

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};

export default configureStore;

And I was using it both in my React index.js and api.js

import configureStore from '~/redux/store';
const store = configureStore();

Changed my default export to a single instance of my configureStore instead of exporting the function

const configureStore = () => {
    const store = createStore(
        rootReducer,
        composeEnhancers(applyMiddleware(...middlewares)),
    );

    sagaMiddleware.run(rootSaga);
    return store;
};
const store = configureStore() // exported this instead;

export default store;

perfect 👍

@neurosnap
Copy link
Member

Closing due to inactivity. If this is still an issue please reply and I will reopen. Thanks!

@geeteshnewput
Copy link

geeteshnewput commented Jun 10, 2022

I am Facing the same issue. and not able to fix by exporting on single line.

@neurosnap
Copy link
Member

Greetings! Can you share some code so we can better understand the problem?

@neurosnap neurosnap reopened this Jun 10, 2022
@neurosnap neurosnap added the bug label Jun 10, 2022
@geeteshnewput
Copy link

geeteshnewput commented Jun 10, 2022

`export default function configureStore(initialState = {}, history) {
// Create the store with two middlewares
// 1. sagaMiddleware: Makes redux-sagas work
// 2. routerMiddleware: Syncs the location/URL path to the state
const middlewares = [sagaMiddleware, routerMiddleware(history)];

const enhancers = [applyMiddleware(...middlewares)];

// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle, indent */
const composeEnhancers =
    process.env.NODE_ENV !== "production" &&
    typeof window === "object" &&
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
        ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
              // Prevent recomputing reducers for `replaceReducer`
              shouldHotReload: false,
          })
        : compose;
/* eslint-enable */
const store = createStore(createReducer(), fromJS(initialState), composeEnhancers(...enhancers));

// Extensions
store.runSaga = sagaMiddleware.run;
store.runSaga(sagas);
store.injectedReducers = {}; // Reducer registry
store.injectedSagas = {}; // Saga registry

// Make reducers hot reloadable, see http://mxs.is/googmo
/* istanbul ignore next */
if (module.hot) {
    module.hot.accept("./reducers", () => {
        store.replaceReducer(createReducer(store.injectedReducers));
    });
}

return store;

}`

and this is for app.js
const store = configureStore(initialState, history);

@neurosnap I added the code
The issue I am facing is, saga is calling API's multiple times at a instant

@neurosnap
Copy link
Member

Can you show us where you are dispatching the action that calls the saga twice? Also, are you using react? What version are you using?

@geeteshnewput
Copy link

Yes I am using react with version: "react": "16.8.6".

This is the dispatch call dispatch(loadTransactions({...filterData}));
and this is the saga call takeLatest(LOAD_TRANSACTIONS, getTransactionsSaga)

@neurosnap
Copy link
Member

Thanks! Could you show us the code that calls the dispatch? I need to understand the full context of your code before diving deeper.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests