reactjsreact-reduxredux-sagareact-lifecycle

How middleware in react life cycle works?


I am new in react js. I have started doing a small product with react-redux. I am using saga middle-ware.

What i have done is as under. This is the component

    //all import work

    import { activateAuthLayout, onLoad } from '../../../store/actions';

    class EcommerceProductEdit extends Component {

        constructor(props) {
            super(props);
            this.state = {
                checked: false,
                unselected_lists: [],
                main_checked: false
            }

            //here I get the products props always null
            console.log(this.props);
        }

        componentDidMount() {
            this.props.activateAuthLayout();

            //dispatching an action to fetch data from api, done in midddleware
            if (this.props.user !== null && this.props.user.shop_id)
                this.props.onLoad({
                    payload: this.props.user
                });
        }

        render() {

            //here I get the products props 
            console.log(this.props);

            return (
            //jsx work
            );
        }
    }

    const mapStatetoProps = state => {
        const { user, is_logged_in } = state.Common;
        const { products, is_loading } = state.Products;
        return { user, is_logged_in, products, is_loading };
    }

    export default withRouter(connect(mapStatetoProps, { activateAuthLayout, onLoad })(EcommerceProductEdit));

Action is

    import { FETCH_PRODUCT, FETCH_PRODUCT_SUCCESS } from './actionTypes';

    export const onLoad = (action) => {
        return {
            type: FETCH_PRODUCT,
            payload: action.payload
        }
    }

    export const productFetched = (action) => {
        return {
            type: FETCH_PRODUCT_SUCCESS,
            payload: action.payload
        }
    }

Reducer is

    import { FETCH_PRODUCT_SUCCESS } from './actionTypes';

    const initialState = {
        products: null,
        is_loading: true
    }

    export default (state = initialState, action) => {
        switch (action.type) {
            case FETCH_PRODUCT_SUCCESS:
                state = {
                    ...state,
                    products: action.payload,
                    is_loading: false
                }
                break;

            default:
                state = { ...state };
                break;
        }
        return state;
    }

And saga is

    import { takeEvery, put, call } from 'redux-saga/effects';
    import { FETCH_PRODUCT } from './actionTypes';
    import { productFetched } from './actions';
    import agent from '../../agent';

    function* fetchProduct(action) {
        try {
            let response = yield call(agent.Products.get, action.payload);
            yield put(productFetched({ payload: response }));
        } catch (error) {
            if (error.message) {
                console.log(error);
            } else if (error.response.text === 'Unauthorized') {
                console.log(error)
            }
        }
    }

    function* productSaga() {
        yield takeEvery(FETCH_PRODUCT, fetchProduct)
    }

    export default productSaga;

I am being able to get the products props only in render function. How would i be able to get it it in constructor ? I would be really grateful if anyone explained me about react life cycle a little bit more. Thanks.


Solution

  • MiddleWare: Middleware just comes in between the flow after the action has been dispatched and before it reaches the reducers, like in your case once you fire onLoad action and before it reaches the reducers, its caught in Saga middleware which executes it according to code written in it

    Lifecycle in your case goes the following way:

    1. In your compoenentDidMount method, you dispatch an action of onLoad. The action type in such a case becomes "FETCH_PRODUCT" and same action is now caught in Saga.

    2. Since this is async call, the code in your component continues executing while the Saga perform its action in parallel. It calls API through this line of code: yield call(agent.Products.get, action.payload); . Once API call is completed, it dispatches an action 'productfetched' through this line of code yield put(productFetched({ payload: response }));.

    3. Now this action reaches reducer and modify the state of "products". Since the product state in your redux is modified, your component EcommerceProductEdit re-renders and you get your product list in render method. The point to be noted is that the flow must have already finished executing inside componentDidMount method by this time, so no chance of having products their

    Solution to your problem:

    1. Once an action is dispatched and which has become async due to Saga, you won't be able to get value in constructor, if you use Saga. You can just directly call upon the API using axios/fetch library in componentDidMount and await their (Making it synchronous). Once you get response, you may proceed further

    2. In case you have functional component, then you may use Effect hook and bind the dependency to products state. You can write your code in this block, what you want to be executed after API call is made and product list modifies.

       React.useEffect(
      
       () => {
           // You code goes here
       },
       [products]
       );