Search code examples
javascriptreactjsreduxreduce

Why is this variable from the Redux store not in the `this.props` on React page?


I have a variable that I believe is correctly written to my Redux store using a Redux action and reducer. But for some reason it never becomes part of the props on a React page. What could be the cause, given the code below? I would expect this.props.transactionDetails and this.state.trans_status to be available within the render code block. The Redux store is functioning correctly for other variables/pages.

React Page:

import { get_details } from "../../../appRedux/actions/transAction";

class DonePage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            showLoader: true,
            transactionDetails: "",
            trans_status: "",
        };
    }

    async componentDidMount() {
        await this.props.get_details('abc1234');
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevProps.transactionDetails !== this.props.transactionDetails) {
            console.log("Inside ComponentDidUpdate");
            // It never arrives here, perhaps suggesting the prop is never created in the store?

            this.setState({
                trans_status: this.props.transactionDetails.trans_status,
            });
        }
    }

    render() {
        console.log(JSON.stringify(this.state.trans_status);
        // returns "" instead of the value of the redux action/reducer
        console.log(JSON.stringify(this.props.transactionDetails);
        // returns undefined instead of the value of the redux action/reducer

        // What am I doing wrong? Why am I not getting the transactionDetails here?
    }
}

const mapStateToProps = (state) => {
    return {
        settings: state.settings,
        // the settings property works fine in this component
        transactionDetails: state.transactionDetails,
    };
};

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators(
        {get_details}, dispatch
    );
};

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(DonePage));

Redux Action:

export const get_details = (trans_id) => {
    let token = getAuthToken();
    return (dispatch) => {
        axios
            .get(`${url}/get_details/${trans_id}`, {
                headers: { Authorization: `${token}` },
            })
            .then((res) => {
                if (res.status === 200) {
                    console.log(JSON.stringify(res.data.data);
                    // returns {"trans_status":"paid","trans_due":"0"}
                    return dispatch({
                        type: DETAILS_SUCCESS,
                        payload: res.data.data,
                    });
                } else {
                    return dispatch({
                        type: DETAILS_ERROR,
                        payload: res.data.data,
                    });
                }
            })
    }
}

Reducer:

import {
    DETAILS_SUCCESS,
    DETAILS_ERROR,
} from "../constants";

const initial_state = {
    transactionDetails: [],
    transactionDetailsError: "",
};

export default function transactionsReducer(state = initial_state, action) {
    switch (action.type) {
        case DETAILS_SUCCESS:
            console.log(JSON.stringify(action.payload));
            // returns {"trans_status":"paid","trans_due":"0"}
            return { ...state, transactionDetails: action.payload };
        case DETAILS_ERROR:
            return { ...state, transactionDetailsError: action.payload };
        default:
            return state;
    }
};

Update In the reducers index file (/reducers/index.js) I have:

const reducers = combineReducers({
    variousVariables: variablesReducer,
    transactions: transactionsReducer,
});

It works if I change this to:

const reducers = combineReducers({
    variousVariables: variablesReducer,
    transactionDetails: transactionsReducer,
});

On the Page, within render, this.props.transactionDetails now returns data:

{"transData":[],"transactionDetails":{"transaction_status":"paid","transaction_amount_due":"0"},"transactionDetailsError":"","otherData":"", etc.}

So transactionDetails is nested inside another transactionDetails and I need to call this.props.transactionDetails.transactionDetails or this.props.transactionDetails.transData. However, I would like this to be this.props.transactions.transactionDetails and this.props.transactions.transData.

Can someone explain what is going on? I thought I could just name the keys in the reducers index file anything I like and that then defines the "path" of this.props.anythingilike.

Why can I not just change the key in the reducers index file to get what I'm looking for? I guess I need to change something on the Page as well to make it the way I want? Because right now if I change the key in the reducers index file to transactions, then this.props.transactions is still undefined on the Page, while if that key is transactionDetails then this.props.transactionDetails does return the correct data.


Solution

  • I found the solution to my question. As suggested by several people there was details about the store setup that were of importance, particularly the setup of the reducers and the use of combineReducers.

    The setup of my reducers included:

    const reducers = combineReducers({
        ...,
        transactions: transactionsReducer,
    });
    

    To make it work, on the Page I only needed to change:

    1--this.props.transactionDetails to this.props.transactions (and this.props.transactions.transactionDetails if needed.

    2--The last line in the code block below needed to be transactions: state.transactions

     const mapStateToProps = (state) => {
         return {
             settings: state.settings,
             transactionDetails: state.transactionDetails,
         };
     };