Component not re-rendering , after reducer call and props not updating? Code Answer

I am trying to fetch some stocks data from a stocks API. i can fetch it using normal react hooks , but i am facing issues when doing it with redux and redux saga. In the first render, it doesn’t update the redux store. It updates the redux store in the second render. but even if the redux store is updated, my component doesnt get the updated state??

Can anyone please Help?

this is my component :

import React, { useState,useEffect } from 'react'
import { connect } from "react-redux";
import { Link } from 'react-router-dom'
function Dashboard(props) {

    useEffect(()=>{
            console.log(props)
            props.getStockData()
    },[])

    useEffect(()=>{
        props.getStockData()
        console.log(props)
    },[props.stocks.length])
  
    return (
        <React.Fragment>
                {props.stocks.length ? 

                    <div> 
                    <h1>hello</h1>
                    </div>
                
                    :   <button class=" p-4 m-4 spinner"></button>
                
                }
        </React.Fragment>
    ) 
   
}
const mapStateToProps = state => {
    return {
      stocks: state
    };
  };
  
const mapDispachToProps = dispatch => {
    return {
      getStockData: () => dispatch({ type: "FETCH_STOCKS_ASYNC" }),
    };
};
export default connect(mapStateToProps,mapDispachToProps)(Dashboard)

This is my reducer file:

import axios from 'axios'
import swal from 'sweetalert'
const initialState = {
    stocks:[],
};
const reducer = (state = initialState, action) => {
    const newState = { ...state };
  
    switch (action.type) {
      case 'FETCH_STOCKS_ASYNC':
        console.log('reducer called')
        const getLatestPrice = async()=>{
            await axios.get(`http://api.marketstack.com/v1/eod/latest?access_key=8c21347ee7c5907b59d3cf0c8712e587&symbols=TCS.XBOM`)
            .then(res=>{
                console.log(res.data)
                newState.stocks = res.data
            })
            .catch(err=>{
                console.log(err)
            })
        }
        getLatestPrice();
        
        break;
    }
    return newState;
};
  
export default reducer;

this is my redux saga file:

import { delay } from "redux-saga/effects";
import { takeLatest, takeEvery, put } from "redux-saga/effects";

function* fetchStockData() {
  yield delay(4000);
  yield put({ type: "FETCH_STOCKS_ASYNC" });
}

export function* watchfetchStockData() {
  yield takeLatest("FETCH_STOCKS_ASYNC", fetchStockData);
}

this is my store file:

import { createStore,applyMiddleware,compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { watchfetchStockData } from './sagas'
import reducers from './reducers'


const sagaMiddleware = createSagaMiddleware()
const store = createStore(
        reducers,
        compose(
                applyMiddleware(sagaMiddleware),
               window.devToolsExtension && window.devToolsExtension()
            ),
       
)

console.log(store.getState())

// only run those sagas whose actions require continuous calling.
//sagaMiddleware.run(watchfetchStockData);


export default store;

this is my index file which connects provider to all the components:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import './assets/main.css'
import App from './App';
import { Provider } from 'react-redux'
import { BrowserRouter as Router } from 'react-router-dom'
import store from './store'
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
  <Provider store={store}>
      <Router>
        <App />
      </Router>
  </Provider>
</React.StrictMode>,
  document.getElementById('root')
);

serviceWorker.unregister();

Answer

You misunderstand how the pieces are supposed to fit together here. I highly recommend that you read the redux best practices and learn more about what a reducer does and does not do. A redux reducer should not perform any actions. It merely takes the current state and returns the next state based on the data in an action object.

Luckily you are already using redux-saga, which is designed to handle side-effects such as API calls. The fetch needs to be executed in your saga, not in the reducer.

An API call typically involves dispatching three actions: start, success, and failure. From your component, you would only ever dispatch the “FETCH_STOCKS_START” action. The saga “takes” this action via takeLatest and uses it to execute the fetch with its call effect. When the fetch is finished, the saga uses put to dispatch one of two result actions. It sends either type “FETCH_STOCKS_SUCCESS” with a property payload containing an array of stocks, or type “FETCH_STOCKS_ERROR” with a property error containing the error.

function* fetchStockData() {
  yield delay(4000);
  try {
    const res = yield call( axios.get, `http://api.marketstack.com/v1/eod/latest?access_key=8c21347ee7c5907b59d3cf0c8712e587&symbols=TCS.XBOM`);
    yield put({ type: "FETCH_STOCKS_SUCCESS", payload: res.data });
  }
  catch (error) {
    yield put({ type: "FETCH_STOCKS_ERROR", error });
  }
}

function* watchfetchStockData() {
  yield takeLatest("FETCH_STOCKS_START", fetchStockData);
}

Relevant saga docs links: Error Handling and Dispatching Actions

I have added an isLoading property to the state, which you can select to show a different UI when true. We will update this property with all three actions.

const initialState = {
  stocks: [],
  isLoading: false
};

Your reducer serves only to update the raw data in the state based on these actions.

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "FETCH_STOCKS_START":
      return {
        ...state,
        isLoading: true
      };
    case "FETCH_STOCKS_ERROR":
      console.error(action.error); // only temporary since you aren't doing anything else with it
      return {
        ...state,
        isLoading: false
      };
    case "FETCH_STOCKS_SUCCESS":
      return {
        ...state,
        stocks: action.payload,
        isLoading: false
      };
    default:
      return state;
  }
};

Related Posts

© No Copyrights, All Questions are retrived from public domain.
Tutorial Guruji