M. R
M. R

Reputation: 55

How can multiple action items be dispatched within a function while using redux-saga as the middleware?

I am using redux-saga as a middleware and i want to dispatch multiple action items to the store. As of now i am able to dispatch only one action (i.e., within the fetchData() function which i am calling in Home.js component).

I've tried adding multiple actions but its not working, Only the first action type is getting dispatched

action.js

    import { FETCH_ABOUT, FETCH_CTA, FETCH_PRODUCTS} from './actionType'
//import axios from 'axios';



export const fetchData = () => (
   
    { type:FETCH_PRODUCTS}
    
)


export const fetchProducts = (products) => ({
    type: FETCH_PRODUCTS,
    payload: products,
})

export const fetchCta = (cta) => ({
    type: FETCH_CTA,
    payload: cta,
})

export const fetchAbout = (about) => ({
    type: FETCH_ABOUT,
    payload: about,
})


reducer.js

    import { FETCH_ABOUT, FETCH_CTA, FETCH_PRODUCTS } from "./actionType"


const initialState = {
    products: [],
    cta:'',
    about:'',
    
}

const productsReducer = (state=initialState,action) => {
    switch(action.type){
        
            case FETCH_PRODUCTS:
                return{
                    ...state,
                    products: action.payload
                }
                case FETCH_CTA:
                    return{
                        ...state,
                        cta: action.payload
                    }
                    case FETCH_ABOUT:
                        return{
                            ...state,
                            about: action.payload
                        }
            default:
                return state;
    }
}

export default productsReducer;


ProductsSaga.js

    import {call,fork,put,takeLatest} from 'redux-saga/effects'
import { fetchCta, fetchProducts } from '../redux/action';
import { FETCH_CTA, FETCH_PRODUCTS } from '../redux/actionType';
import { fetchAPIcall } from '../redux/api'


function* fetchData() {
    try{
        const { data } = yield call(fetchAPIcall);
        console.log(data.data.productCopy);
        yield put(fetchProducts(data.data.productCopy));
    }catch(e){
        console.log(e);
    }
}

//watcher saga
export function* watcherSaga() {
    yield takeLatest(FETCH_PRODUCTS,fetchData)
}

export const productsSaga = [fork(watcherSaga)]


ctaSaga.js

    import { call,put,takeLatest,fork } from "redux-saga/effects";
import { fetchCta } from "../redux/action";
import { FETCH_CTA } from "../redux/actionType";
import { fetchAPIcall } from "../redux/api";


function* onFetchCta() {
    try{
        const { data } = yield call(fetchAPIcall);
        console.log(data.data.cta);
        yield put(fetchCta(data.data.cta));
    }catch(e){
        console.log(e);
    }
}

//watcher saga
export function* watcherSaga() {
    yield takeLatest(FETCH_CTA,onFetchCta)
}

export const ctaSaga = [fork(watcherSaga)]



Home.js

    import React, { useEffect, useState } from 'react'
import './Home.css'
import {useHistory} from 'react-router-dom';
import { useSelector,useDispatch } from 'react-redux';
import {fetchData} from './redux/action'


const Home = () => {
  const history = useHistory();

const {products,cta} = useSelector((state)=>(state.productsReducer));
console.log(products,cta);



const dispatch = useDispatch();

useEffect(()=>{
  dispatch(fetchData());
},[])

const productDetail = (item,i) => {
history.push({
  pathname:`product-detail/${i}`,
  state:item
})
}

  return (
    <div className='container'>
 <div className='product'>
      {products.map((item,i) =>{
        return(
          <div key={item.id}>
          <img src={item.Image.path} alt = {item.Image.alt}/>
          <p>{item.title}</p>
          <button onClick={()=>productDetail(item,i)}type='button'>{cta}</button>
         
        </div>
        )
      })}
    </div>
    </div>
   
  )
}

export default Home

Upvotes: 0

Views: 806

Answers (1)

Martin Kadlec
Martin Kadlec

Reputation: 4975

Action creators such as fetchData will always create only a single action object. Also the dispatch function (or put effect) can always dispatch only a single action, however nothing is preventing you from dispatching multiple actions one after the other:

function* mySaga() {
  yield put(firstAction())
  yield put(secondAction())
  yield put(thirdAction())
}
// or
function MyComponent() {
  const dispatch = useDispatch()
  return <div onClick={() => {
    dispatch(firstAction())
    dispatch(secondAction())
    dispatch(thirdAction())
  }}>Hello</div>
}

If you are worried about rerenders, react-redux has the batch function that allows you to wrap your dispatches making sure that react batches all the updates and rerenders only a single time. Note that this is not necessary starting from React 18 as it batches things automatically.

It is more difficult to use the batched updates with redux saga due to its internal scheduler that doesn't guarantee that everything will happen in single tick, but again starting from React 18 you don't need to worry about this. In case you really need it, there are libraries out there that allows you to do it though, check out Mark's post about it which includes links to some of these libraries: https://blog.isquaredsoftware.com/2020/01/blogged-answers-redux-batching-techniques/

One more thing, if you find yourself dispatching the same list of actions again and again, maybe it make sense to merge these together to a single action and avoid the issue entirely.

Upvotes: 1

Related Questions