shorif2000
shorif2000

Reputation: 2654

reactjs Uncaught Error: Actions must be plain objects. Use custom middleware for async actions

I am pretty sure i am returning an object and have used asyn and await on the promise within my action file. but this still keeps returing the error redux.js:205 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions

https://codesandbox.io/s/frosty-nash-wdcjf?fontsize=14

my action file is returning an object

import axios from "axios";

export const LOAD_URL_STATUS = "LOAD_URL_STATUS";

export async function loadUrlStatus(url) {
  const request = await axios
    .get(url)
    .then(response => {
      console.log(response.status);
      return response.status;
    })
    .catch(error => {
      console.log("Looks like there was a problem: \n", error);
    });
  console.log(request);
  console.log(LOAD_URL_STATUS);
  return {
    type: LOAD_URL_STATUS,
    payload: request
  };
}

it fails when calling this action in componenDidMount this.props.loadUrlStatus(url);

component

import React from 'react';
import TrafficLight from '../TrafficLight';
import {connect} from 'react-redux';
import {loadUrlStatus} from "../../actions";
//import {withPolling} from "../Polling";
//import Polling from "../Polling/polling";
import { bindActionCreators } from 'redux';

class TrafficLightContainer extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      redOn: true,
      yellowOn: false,
      greenOn: false,
    }
  }

  componentDidMount(){
console.log("componentDidMount")
const {pollingAction, duration, url} = this.props
    //withPolling(this.props.loadUrlStatus(this.props.url),1)
    /*
    const {pollingAction, duration, url} = this.props
    this.dataPolling = setInterval(
                    () => {
                        this.props.loadUrlStatus(url);
                    },
                    10000);
*/
this.props.loadUrlStatus(url);
  };

  componentWillUnmount() {
                clearInterval(this.dataPolling);
            }

  render() {
    console.log(this.props)
    return (
      <TrafficLight
        Size={100}
        onRedClick={() => this.setState({ redOn: !this.state.redOn })}
        onGreenClick={() => this.setState({ greenOn: !this.state.greenOn })}
        RedOn={this.state.redOn}
        GreenOn={this.state.greenOn}
      />
    )
  }
}

const mapStateToProps = state => ({
    ...state
});
const mapDispatchToProps = dispatch => {
  return bindActionCreators(
    {
        loadUrlStatus
    },
    dispatch
  );
};

export default (
    connect(mapStateToProps, mapDispatchToProps)(TrafficLightContainer));

index

import React from 'react';
import { render } from 'react-dom'
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import configureStore from './configureStore'

const store = configureStore();

const renderApp = () =>
  render(
        <Provider store={store}>
                <App />
        </Provider>,
        document.getElementById('root')
  );



if (process.env.NODE_ENV !== 'production' && module.hot) {
  module.hot.accept('./App', renderApp)
}

renderApp();

serviceWorker.unregister();

Upvotes: 0

Views: 82

Answers (2)

vadersfather
vadersfather

Reputation: 9289

If you're using await in an action creator, you'll want to return a function from the action creator. Otherwise, return on object. A library like redux-thunk will help you do just that.

Your action creator would then look like this:

import axios from "axios";

export const LOAD_URL_STATUS = "LOAD_URL_STATUS";

export const loadUrlStatus(url) => async dispatch => {
  try {
    const response = await axios(url)

    dispatch({
      type: LOAD_URL_STATUS,
      payload: response.status
    })
  } catch (error) {
    // dispatch error
  }
}

Upvotes: 0

Fyodor Yemelyanenko
Fyodor Yemelyanenko

Reputation: 11848

The problem is that loadUrlStatus is async function, so it returns not object, but Promise, and object inside it promise.

To correct this, modify loadUrlStatus so it return another function. As you already applied thunk middleware during store creation, such function will be called inside redux. (You can see samples of async functions here)

export function  loadUrlStatus(url) {
    // Immediately return another function, which will accept dispatch as first argument. It will be called inside Redux by thunk middleware
    return async function (dispatch) {
        const request = await axios
            .get(url)
            .then(response => {
                console.log(response.status);
                return response.status;
            })
            .catch(error => {
                console.log("Looks like there was a problem: \n", error);
            });
            console.log(request);
            console.log(LOAD_URL_STATUS);
            dispatch ({
                type: LOAD_URL_STATUS,
                payload: request
            });
    }
}

Upvotes: 1

Related Questions