Archit Sandesara
Archit Sandesara

Reputation: 655

How to access multiple returns from the reducer using single subscriber in Redux

I am new to Redux and I am having trouble understanding how to get the response of the function call handled by the reducer.

I am doing dispatch({ type: CREATE_PURCHASE_ORDER, payload: response.data.data }); on the response of the API call which is handled by PurchaseOrderReducer.js file.

However, I am not able to get the return from the reducer file PurchaseOrderReducer.js to the ManageShipment.js file where I am calling the await this.props.createPurchaseOrder(user, newPurchaseOrder).

I understand the flow from index.js -> PurchaseOrderReducer.js but I am not clear about the flow from PurchaseOrderReducer.js -> ManageShipment.js .

So I am using mapStateToProps and setting purchaseOrders:state.purchaseOrder in it in ManageShipment.js file, which was giving me return from GET_PURCHASE_ORDERS from PurchaseOrderReducer.js when I do this.props.purchaseOrders when I had only GET_PURCHASE_ORDERS .

But now I added CREATE_PURCHASE_ORDER as well so how to I distinguish that when I do this.props.purchaseOrders it will be the return from GET_PURCHASE_ORDERS or CREATE_PURCHASE_ORDER from PurchaseOrderReducer.js -> ManageShipment.js file?


Here's `index.js` file code:
export const getPurchaseOrders = (user) => async dispatch => {
    try {
        const options = HEADER_OPTIONS_AUTH(user.token);
        const response = await axios.get(`${BASE_URL}/purchase_order`, options);
        if (response.status === 200) {
            const purchaseOrders = response.data.data;
            dispatch({ type: GET_PURCHASE_ORDERS, payload: purchaseOrders });

        }
    }
    catch (e) {
        dispatch({ type: GET_PURCHASE_ORDERS, payload: [] });
    }
};

export const createPurchaseOrder = (user, data) => async dispatch => {
    try {
        const options = HEADER_OPTIONS_AUTH(user.token);
        const response = await axios.post(`${BASE_URL}/insertNewPurchaseOrder`, data, options);
        if (response.status === 201) {
            dispatch({ type: CREATE_PURCHASE_ORDER, payload: response.data.data });
        }
    }
    catch (e) {
        dispatch({ type: CREATE_PURCHASE_ORDER, payload: [] });
    }
};

Here's purchaseOrderReducer.js file code:

import { GET_PURCHASE_ORDERS,CREATE_PURCHASE_ORDER} from '../actions/types';
export default function (state = false, action) {
    switch (action.type) {
        case GET_PURCHASE_ORDERS:
            return action.payload;
        case CREATE_PURCHASE_ORDER:
            return [...state, ...action.payload]
     
        default:
            return state;
    }
}

In the index.js file for reducer I am doing:

import purchaseOrderReducer from './purchaseOrderReducer'

export default combineReducers({
 purchaseOrder: purchaseOrderReducer,
});

And in the ManageShipment.js file code:

class ManageShipment extends Component {
  constructor(props) {
    super(props);
}
componentDidMount() {
    this.props.getPurchaseOrders(user);
}

render(){

 const purchaseOrders = this.props.purchaseOrders;

  const creatPurchaseOrder = async(user,newPurchaseOrder) =>{
  await this.props.createPurchaseOrder(user, newPurchaseOrder); // How can I get the response of this post api here?
  }
  
}

}

const mapStateToProps = state => {
  return {
    purchaseOrders: state.purchaseOrder,
  };
};

export default connect(mapStateToProps, actions)(ManageShipment);

Upvotes: 0

Views: 135

Answers (2)

Linda Paiste
Linda Paiste

Reputation: 42298

If you read up a bit more on Redux best practices you will see that you are approaching this from the wrong angle.

A dispatch function dispatches an action which causes a change to the store. It should not return anything. You've having trouble figuring out how to do this because you are trying to do something that redux is not designed to do.

Instead of await-ing for a return value from the dispatch, what you should be doing a using a selector to select the information that you need from the store. By using mapStateToProps (or the useSelector hook, as hooks are the preferred method nowadays), the selector will update with the new value as soon as it has been put in the store by the dispatch.

As far as detecting which order is the new order, there's a couple things you can do. Does each purchase order have a unique id? Is the id something that you know at the time when you are dispatching the action? If this is the case, that would be the easiest. You would select all purchaseOrders from state and look for the one which matches the id that you posted.

Do you actually need to know which purchase order is the new one? Or do they all get rendered the same way?

You absolutely should not call a dispatch from inside the render() function, which seems to be what you are doing now. It should be called in response to an action (onClick, etc.) or in componentDidMount.

Here's how it might look as a function component

export const ManageShipment = ({user}) => {

    // selects all order from the state.  will update when new orders are added in the state
    const orders = useSelector( state => state.purchaseOrder );

    // access the dispatch function
    const dispatch = useDispatch();

    // create a function to call the createPurchaseOrder action
    const onClickButton = () => {
        dispatch(createPurchaseOrder(user, data)); // where does this data come from?
    }

    // use effect to fetch orders on component mount
    useEffect( () => {
        dispatch( getPurchaseOrders( user ) );
    }, []) // empty dependency array means this is only fetched once on component mount

    // might initially render an empty array of orders, but will get updated with the actual orders
    return (
        <div>
            <h2>Your Orders</h2>
            {orders.map((order, i) => (
                <PurchaseOrder 
                    key={order.id}
                    order={order}
                />
            ))}
            <button onClick={() => onClickButton}>Create New Order</button>
        </div>
    )
}

Upvotes: 1

elahe karami
elahe karami

Reputation: 693

Well, I'm not sure what you are doing exactly, especially in your reducer. but remember that you are using Redux. In createPurchaseOrder() function you are calling purchaseOrderReducer.js reducer which will fill purchaseOrder for you with the response from API. if you use combinedReducers correctly and create a store you can easily access purchaseOrder in your ManageShipment.js using react-redux methods like connect as you have done ManageShipment.js. if you want the response you can use the object you have created with mapStateToProps. You can set a loading state and set it to false when your async method is done and after that, your purchaseOrder is filled with the response data you need.

And as much as I know, in index.js file for reducer, you should export store, not combineReducers. something like below:

const rootReducer = combineReducers({
      purchaseOrder: purchaseOrderReducer,
});

const store = createStore(rootReducer)
store.subscribe(() => {
    console.log(store.getState())
})
export default store

then in index.js of your react app you should wrap all of your app in Provider component imported from react-redux package. and after that, you can access purchaseOrder where you want using connect or useSelector. something like this:

React index.js should look like this:

import {Provider} from "react-redux"
import store from "./redux" //from index.js in your redux files

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>, 
    document.getElementById("root")
)

wish this answer help you somehow.

Upvotes: 2

Related Questions