Pooja Kushwah
Pooja Kushwah

Reputation: 187

How to dispatch multiple actions from same component?

What I'm trying to do is, dispatching 2 actions fetchBrand() and fetchDistance(). I'm having two different reducers and different action creators who are calling different APIs.

But the issue here is, it is giving the result of only one action depending on the sequence in useEffect(). It is displaying in the console two times but results from the same API.

If a change the sequence then it will console the result for other API two times. That means my API call is getting successful for both but at a time only 1 API is calling.

It might be some functionality, but I'm not aware. How can I fix it?

import { Form, Input, Button, Select } from 'antd'
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'

import { fetchDistance } from '../redux/distanceAction'
import { fetchBrand } from '../redux/brandAction'

const { Option } = Select
const layout = {
  labelCol: {
    span: 8,
  },
  wrapperCol: {
    span: 10,
  },
}

const tailLayout = {
  wrapperCol: {
    offset: 10,
    span: 10,
  },
}

const UserForm = (props) => {
  const distanceList = useSelector((state) => state.distance)
  const brandList = useSelector((state) => state.brand)

  const dispatchDistance = useDispatch()
  const dispatchBrand = useDispatch()

  React.useEffect(() => {
    dispatchBrand(fetchBrand()) //dispatching action one
    dispatchDistance(fetchDistance()) //dispatching action 2
  }, [])

  const [form] = Form.useForm()

  const onFinish = (values) => {
    console.log(values)
    props.onSubmitForm(values)
    form.resetFields()
    console.log(brandList.brand)
    console.log(distanceList.distance)
    // dispatch(fetchBrand);
    // dispatch(fetchDistance);
  }

  return (
    <div className="form-layout">
      <h1> hello{distanceList.distance + brandList.brand}</h1>

      <Form {...layout} form={form} name="user-form" onFinish={onFinish}>
        <Form.Item name="name" label="Hotel Name" rules={[{ required: true }]}>
          <Input />
        </Form.Item>

        <Form.Item
          name="location"
          label="Hotel Location"
          rules={[{ required: true }]}
        >
          <Input />
        </Form.Item>

        <Form.Item name="distance" label="Distance" rules={[{ required: true }]}>
          <Select placeholder="Please select Distance" allowClear>
            <Option value="0km">0Km</Option>
            <Option value="10km">10Km</Option>
            <Option value="30">30km</Option>
          </Select>
        </Form.Item>

        <Form.Item name="brand" label="Brands" rules={[{ required: true }]}>
          <Select placeholder="Please select Brand" allowClear>
            <Option value="flip">Flipkart</Option>
            <Option value="amazon">Amazon</Option>
            <Option value="paytm">Paytm</Option>
          </Select>
        </Form.Item>

        <Form.Item {...tailLayout}>
          <Button type="primary" htmlType="submit">
            Submit
          </Button>
        </Form.Item>
      </Form>
    </div>
  )
}

export default UserForm

Upvotes: 1

Views: 12162

Answers (2)

Ajeet Shah
Ajeet Shah

Reputation: 19863

As suggested in the other answer, you can use something more modern library like RTK to reduce boilerplate code. But if you want to fix your code, all you need to do is create unique action types constants. You can do so by namespacing (e.g brand/FETCH_SUCCESS etc.) them:

Action Creators:

export const fetchrequest = (name) => {
  return {
    type: name + FETCH_REQUEST,
  };
}
...

Reducer:

export const name = "brand/";

export const brandReducer = (state = initialState, action) => {
  switch (action.type) {
    case name + FETCH_SUCCESS:
      return {
        ...state,
        loading: false,
        brand: action.payload,
        error: "",
      }
    ...

  }
};

Actions:

import { name } from "./brandReducer";

export const fetchBrand = () => {
  return function (dispatch) {
    dispatch(fetchrequest(name));
    axios
      .get(URL_HERE)
      .then((response) => {
        const brand = response;
        dispatch(fetchsuccess(name, brand));
      })
      ...

  };
};

You need to do similar for distance actions/reducer as well.

PS: You need just one const dispatch = useDispatch().

Upvotes: 1

markerikson
markerikson

Reputation: 67577

My first observation is that you don't need to call useDispatch() twice, nor do you need to have separate dispatch-named variables. There's only one dispatch function for the entire application, because there's only one Redux store for the entire app. So, you just need const dispatch = useDispatch().

Second, the actual logic looks fine and should work okay with or without the fixed dispatch names. So, I'm not clear what the actual problem is here, and I'd recommend double-checking the implementation of those fetching functions.

update

Ah, I think I see the issue.

Both fetchBrand and fetchDistance are dispatching the same action type after the API call succeeds: dispatch(fetchsuccess(someData)). Both reducers are listening for that same action type. So, both reducers are going to end up saving the first data type that comes back, then overwriting it with the second data type that comes back.

You need to have different action types for "fetched brand success" and "fetched distance success".

The other thing to note here is that the style of Redux code you're writing is outdated and going to require writing a lot more code by hand than you should. Our official Redux Toolkit package will simplify all of this code that you've got. For example, createAsyncThunk will generate those unique action types for success/failure for each type of data that you're wanting to fetch.

Please read the Redux Fundamentals: Modern Redux with Redux Toolkit tutorial page in our docs to see how to use Redux Toolkit correctly.

Upvotes: 0

Related Questions