Enrico
Enrico

Reputation: 830

Redux Thunk for simple async requests

I am building a simple app to retrieve some recipes from an API URL. I am reading the documentation of Thunk to implement it but I cannot understand how to set the async get request. What is strange is that if I console.log the action passed into the reducer it definitely retrieves the correct object (a list of recipes for shredded chicken). When I pass the action onto the the reducer, instead, it throws the error:

"Unhandled Rejection (Error): Given action "FETCH_RECIPES", reducer "recipes" returned undefined. To ignore an action, you must explicitly return the previous state. If you want this reducer to hold no value, you can return null instead of undefined."

Is there any error in my action creator? is the API call properly done?

Store

import React from 'react';
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import SearchBar from './components/App';
import thunk from 'redux-thunk';
import 'bootstrap/dist/css/bootstrap.css';



import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware(thunk));

render(
    <Provider store={store}>
      <SearchBar />
    </Provider>,
    document.getElementById('root')
  )

Component

import React, { Component } from 'react';
import { connect } from 'react-redux';
import './App.css';
import { Button } from 'reactstrap';
import { Form } from 'reactstrap';
import { bindActionCreators } from 'redux';
import  {fetchRecipe } from '../actions';

class SearchBar extends Component {
  constructor(props) {
    super(props)


    this.state = { term: ''};
    this.typeRecipe = this.typeRecipe.bind(this)
    this.onFormSubmit = this.onFormSubmit.bind(this);  
  }

  onFormSubmit(e) {
    e.preventDefault()

    this.props.fetchRecipe(this.state.term)
  }


  typeRecipe(e) {
    this.setState({term: e.target.value});

  }

  render() {
    return (
      <div className="SearchBar">
         <Form onSubmit={this.onFormSubmit}>
            <input type='text'
            value={this.state.term}
            placeholder='ciao'
            onChange={this.typeRecipe}
             />
             <br/>
            <Button id='ciao' className='btn-success'>Submit</Button>
         </Form>
      </div>
    );
  }
}


function mapDispatchToProps(dispatch) {
  return bindActionCreators({ fetchRecipe }, dispatch);
}


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

Action creator

import axios from 'axios';

export const FETCH_RECIPES = 'FETCH_RECIPES';


const API_KEY = 'xxx';//not the real one.

export function fetchRecipe() {
   const request = axios.get(`http://food2fork.com/api/search?key=${API_KEY}&q=shredded%20chicken`);

   return (dispatch) => {
    request.then(({data}) =>{
        dispatch({ type: FETCH_RECIPES, payload: data})
    })
}


}

reducer

import { FETCH_RECIPES } from '../actions';

export default function (state = {}, action) {
    switch(action.type) {
        case FETCH_RECIPES:
            const newState = action.payload.data;                    
            return newState;
        default:
            return state
 }
}

combineReducer (index)

import recipeReducer from '../reducers/recipes_reducer';
import { combineReducers } from 'redux';

const rootReducer = combineReducers({
    recipes: recipeReducer 
});

export default rootReducer;

Upvotes: 0

Views: 242

Answers (1)

McRist
McRist

Reputation: 1748

Mistake is in the reducer return statement.

export default function (state = {}, action) {
    switch(action.type) {
        case FETCH_RECIPES:
            const newState = {...state, data : action.payload.data};                    
            return newState;
        default:
            return state
 }
}

Here we are adding data key to the reducer state, to access this you can use do this in you container :

export default connect((state)=>{
    var mapStateToProps = {};
    if(state.recipes.data) {
        mapStateToProps['recipes'] =  state.recipes.data
    }
    return mapStateToProps;    

 }, mapDispatchToProps)(SearchBar);

and recipes data will be available as this.props.recipes.

Upvotes: 1

Related Questions