0

I'm using React and Redux to build an app to request and subsequently display movie information from an API. In the console, I can get the requested data back but somewhere beyond that I hit the error - "Error: Actions must be plain objects. Use custom middleware for async actions."

Here's my code so far..

Search component:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchMovie } from '../../actions/index';

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

    this.state = { term: '' };

    this.onInputChange = this.onInputChange.bind(this);
    this.onFormSubmit = this.onFormSubmit.bind(this);
  }

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

  onFormSubmit(event) {
    event.preventDefault();
    this.props.fetchMovie(this.state.term); 
    this.setState({ term: '' });
  }

  render() {
    return (
      <div>
        <form onSubmit={this.onFormSubmit} className="input-group">
          <input
            placeholder="Search by movie title, actor, or genre"
            className="form-control"
            value={this.state.term}
            onChange={this.onInputChange}
          />
          <span className="input-group-btn">
            <button type="submit" className="btn btn-secondary">
              Submit
            </button>
          </span>
        </form>
        <br />
      </div>
    );
  }
}

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

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

Action...

import axios from 'axios';

const API_KEY = '449a384f';

export const FETCH_MOVIE = 'FETCH_MOVIE';

let movies = [];

export function fetchMovie(term) {
  const request = axios
    .get(`http://www.omdbapi.com/?s=${term}&apikey=${API_KEY}`)
    .then(response => {
      movies = response.data;
      response.json = movies;
    })
    .catch(error => console.log(error));

  return {
      type: FETCH_MOVIE,
      payload: request,
    };
}

Reducer...

import { FETCH_MOVIE } from '../actions/index';

export default function(state = null, action) {
  switch (action.type) {
    case FETCH_MOVIE:
      return [action.payload.data, ...state];
  }
  return state;
}

CombineReducer...

import { combineReducers } from 'redux';
import MovieReducer from './movie_reducer';

const rootReducer = combineReducers({
  movie: MovieReducer,
});

export default rootReducer;

Store...

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
// import ReduxThunk from 'redux-thunk';
import { BrowserRouter, Switch, Route } from 'react-router-dom';

import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';

import './index.css';
import App from './App';
import Login from './components/login/login';
import reducers from './reducers/reducer';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import { findDOMNode } from 'react-dom';
import $ from 'jquery';

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <BrowserRouter>
      <Switch>
        <Route path="/Login" component={Login} />
        <Route path="/" component={App} />
      </Switch>
    </BrowserRouter>
  </Provider>,
  document.querySelector('#root')
);

Thanks for the help!

  • You should be using thunks or sagas with async actions. See this: https://stackoverflow.com/questions/44166226/actions-must-be-plain-objects-while-using-redux. – Karel Tamayo Jan 20 '18 at 20:25
  • I've been using a tutorial to help me with this project and they did not use thunk or sagas. Is there a way to just change the part that is "async" to be not async? – Brian Poole Jan 20 '18 at 20:35

2 Answers2

0

Vanilla Redux requires that you return a plain JavaScript object in your action creators. Whenever, you need to perform async operations, you need to introduce middleware like redux-thunk or redux-promise to intercept the returned object an perform additional work so that a plain JavaScript object can ultimately be returned.

You're attempting to use redux-promise, but what you're returning is not causing the middleware to be invoked. Your fetchMovie() method is returning a plain object containing a Promise. In order to use redux-promise, the method needs to return a Promise.

From their documentation:

createAction('FETCH_THING', async id => {
  const result = await somePromise;
  return result.someValue;
});
Adam Kipnis
  • 10,175
  • 10
  • 35
  • 48
0

Probably, the reason is that you're trying to return promise in the action to reducer.

You're using thunk, so you always can dispatch from action creator

export const fetchMovie = term => dispatch => axios
  .get(`http://www.omdbapi.com/?s=${term}&apikey=${API_KEY}`)
  .then(response => {
    dispatch({
      type: FETCH_MOVIE,
      payload: response,
    });
  })
  .catch(error => console.log(error)); 
Alexey Avdeyev
  • 599
  • 9
  • 20