0

I'm working on an application using react and redux. I use api.

application flow:

  • fill the form,
  • click the send button,
  • send data from the form to the api,
  • go to the recipes page

The first component is the form to which you enter information (name, calories, type of diet).

    class FormPage extends Component {
      constructor(props) {
        super(props);

        this.handleFormSubmit = this.handleFormSubmit.bind(this);
        this.goToListOfMealPage = this.goToListOfMealPage.bind(this);
      }

      handleFormSubmit(data) {
        const name = data.name;
        const calories = data.caloreis;
        const diet = data.diet;
        const health = data.health;

        console.log(name)
        return loadData( name, calories, diet, health)()
          .then(({ error }) => {
            if (!error) {
              setTimeout(this.goToListOfMealPage, 1500);
            }

            return error;
          }
        );
      }

      goToListOfMealPage() {
        const { history } = this.props;
        history.push('/ListMeal');
      }

      render() {
        return (
          <Form onSubmit={this.handleFormSubmit}/>
        );
      }
    }

const mapDispatchToProps = (dispatch) => {
  return {
    loadData: () => dispatch(loadData())
  }
};

FormPage = connect(mapDispatchToProps)(FormPage)
export default FormPage;

handleFromSubmit function is to send form data to the api link (https://api.edamam.com/search?q=${name}n&app_id=${key.id}&app_key=${key.key}&calories=${calories}&health=${health}&diet=${diet}).

After filling in the form and after clicking the send button, I want to have a list of meals (recipes) on the new subpage.

where loadData is

const fetchDataStart = () => ({
  type: actionTypes.FETCH_DATA_START,
});

const fetchDataSucces = (data) => ({
  type: actionTypes.FETCH_DATA_SUCCESS,
  data,
});

const fetchDataFail = () => ({
  type: actionTypes.FETCH_DATA_FAIL,
});

const loadData = (name, calories, diet, health) => (dispatch) => {
  dispatch(fetchDataStart());
  return axios.get(`https://api.edamam.com/search?q=${name}n&app_id=${key.id}&app_key=${key.key}&calories=${calories}&health=${health}&diet=${diet}`)
    .then(({ data }) => console.log(data) || dispatch(fetchDataSucces(data)))
    .catch((err) => dispatch(fetchDataFail(err.response.data)));
};

After sending the form, I get an error TypeError: dispatch is not a function

enter image description here

I can not find the reason for this error

2
  • 1
    Can you provide mapDispatchToProps as well? loadData(name, calories, diet, health)() - currently dispatch is undefined Commented Mar 19, 2019 at 10:13
  • @BrianLe I edited my post. const mapDispatchToProps = (dispatch) => { return { loadData: () => dispatch(loadData()) } }; Commented Mar 19, 2019 at 10:21

2 Answers 2

5

There are some problems with your code:

  • If you have mapped the dispatch to prop, you can invoke the action by doing this.props.loadData(params)
  • You should not invoke the action by doing this loadData()() as the dispatched action does not return a function (don't let it trick you although the original action returns a function).

So, to use loadData() action, you need to map it to props like so:

const mapDispatchToProps = dispatch => ({
  loadData: (name, calories, diet, health) => dispatch(loadData(name, calories, diet, health)),
});

Then use it like so:

componentDidMount() {
  this.props.loadData(name, calories, diet, health)
    .then(() => console.log('Success'))
    .catch(err => throw new Error("Error", err.stack))
}

Edit: Based on your newly edited question, the connect function in redux accepts mapStateToProps and mapDispatchToProps respectively, so in your code it should be:

export default connect(null, mapDispatchToProps)(Component)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the edit. That saved me many hours of debugging.
1
  1. Your constructor is not needed - you can auto-bind the functions this way.

  2. If there's no mapStateToProps in the component, maintain it as null.

EDITED CODE:

import React from 'react';
// Your imports

class FormPage extends Component {
  handleFormSubmit = (data) => {
    const { name, caloreis, diet, health } = data;
    this.props.loadData(name, caloreis, diet, health);
  }

  goToListOfMealPage = () => {
    const { history } = this.props;
    history.push('/ListMeal');
  }

  render() {
    return (
      <Form onSubmit={this.handleFormSubmit} />
    );
  }
}

const mapDispatchToProps = dispatch => ({
  loadData: (name, caloreis, diet, health) => dispatch(loadData(name, caloreis, diet, health))
});

export default connect(null, mapDispatchToProps)(FormPage);

Suggestion on redirect:

  1. You have to maintain success and error of submit in redux state, if success - you can redirect to goToListOfMealPage - You can do this in componentWillReceiveProps. We should do something similar to the below code:
class FormPage extends Component {
  componentWillReceiveProps(nextProps) {
    if (this.props.formSubmitSuccess !== nextProps.formSubmitSuccess && nextProps.formSubmitSuccess) {
      this.goToListOfMealPage()
    }
  }
  //... rest of the code.
}

// Your map state to props:
const mapStateToProps = state => ({
  formSubmitSuccess: state.reducerIdentifier.formSubmitSuccess,
  formSubmitFailure: state.reducerIdentifier.formSubmitFailure
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.