0

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.

Tekraj Shrestha
  • 1,228
  • 3
  • 20
  • 48

4 Answers4

2

a constructor is called during object instantiation. According to the docs "The constructor for a React component is called before it is mounted". So if the props passed to the component are being changed after the component has been mounted you can use componentWillReceiveProps life cycle methods.

componentWillReceiveProps is deprecated so you can use componentDidUpdate instead. Example from the docs.

componentDidUpdate(prevProps) {
  // Typical usage (don't forget to compare props):
  if (this.props.userID !== prevProps.userID) {
    // update your component state from here.
    this.fetchData(this.props.userID);
  }
}
Naresh
  • 941
  • 9
  • 22
1

You just have to console props rather than doing this.props. You should not reference props with this inside the constructor.

Do this instead:

console.log(props)

Darsh Shah
  • 351
  • 3
  • 9
1

Middleware is not related to react lifecycle at all, other than it updates and connected components "react" to props updating.

Check the constructor docs

https://reactjs.org/docs/react-component.html#constructor

Question: why are you trying to log props in the constructor anyway? If you want to know what the props are, use one of the lifecycle functions, componentDidMount/componentDidUpdate, don't use the render function to do side-effects like make asynchronous calls or console log.

componentDidMount() {
  console.log(this.props);
}

If you must log props in the constructor though, access the props object that was passed as the component won't have a this.props populated yet.

constructor(props) {
  super(props);
  ...
  console.log(props);
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • I am trying to implement the product list table, where there will be checkbox functionality to record the selected products only. https://stackoverflow.com/questions/62643684/implementing-check-boxes-in-react In above link is what i am trying to do. But i am not getting all things to put together. Thanks you for your answer, at least I got some Idea. Would you be able to look to the link once ? Thank you Sir. – Tekraj Shrestha Jun 30 '20 at 06:18
  • Also in componentDidmount this.props.products is null when consoled after dispatching onload action. I have attached a screenshot above. Thanks – Tekraj Shrestha Jun 30 '20 at 06:28
1

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]
     );
    
Raghvender Kataria
  • 1,457
  • 7
  • 14
  • Thank you so much for the answer. In my case i have used class based component. so may be, i need to go with solution one here. Also i would be really grateful to you if you could visit this link https://reactjs.org/docs/react-component.html#constructor and help to achieve the tasks there. Thanks – Tekraj Shrestha Jun 30 '20 at 06:34
  • You need to avoid saga and go with synchronous API call using fetch/axios library directly. In that case you can get response directly in constructor/componentDidMount (componentDidMount is recommended for API calls) – Raghvender Kataria Jun 30 '20 at 06:42
  • you mean I have to dump the idea of using redux ? Because with promise middleware also, if I dispatch the action from component did mount, I need to update the state of reducer which then will be available in render function only. But if I directly called the api without dispatching action, then I get the value synchronously, but then i need to remove the usage of middleware. – Tekraj Shrestha Jun 30 '20 at 07:30
  • You may still use redux and dispatch the action after getting response of API synchronously without using saga for this case. You may then be able to get the products in constructor/componentdidmount itself and then you may do what is desired – Raghvender Kataria Jun 30 '20 at 07:35
  • yeah, that idea never came across my mind. Thank you so much. But what is the actual reason of using midleware then ? I mean, we could handle things in componentDidMount itself. I am just curious, am I doing the things correctly ? Do I even need redux ? If you visit the above link you will understand I what i actually want to achieve on this component. Answer is already given by @Drew Reese. You could just help me by telling if its worthy implementing react-redux or would be more productive with just react. Thanks – Tekraj Shrestha Jun 30 '20 at 07:43