简体   繁体   中英

Error: Actions must be plain objects. Use custom middleware for async actions, in a delete button?

I am trying to get a react action to fetch a list of files after the user deletes a file from the list.

In App.js I pass a handleClick function to the nested component.

App.js

    class App extends Component {
    static propTypes = {
        files: PropTypes.array.isRequired,
        isFetching: PropTypes.bool.isRequired,
        dispatch: PropTypes.func.isRequired,
        handleClick : PropTypes.func
    };

    componentDidMount() {
        const {dispatch} = this.props;
        dispatch(fetchFiles);
    }

    handleClick = fileId => {
        const {dispatch} = this.props;
        deleteFileById(dispatch,fileId);
    };

    render() {
        const {files, isFetching, dispatch} = this.props;
        const isEmpty = files.length === 0;
        return (
            <div>
                <h1>Uploadr</h1>
                {isEmpty
                    ? (isFetching ? <h2>Loading...</h2> : <h2>No files.</h2>)
                    : <div style={{opacity: isFetching ? 0.5 : 1}}>
                        <Files files={files} handleClick={this.handleClick}/>
                    </div>
                }
            </div>
        )
    }
}

const mapStateToProps = state => {
    const {isFetching, items: files} = state.files;

    return {
        files,
        isFetching,
    }
};


export default connect(mapStateToProps)(App)

Files.js

import React from 'react'
import PropTypes from 'prop-types'

const Files = ({files, handleClick }) => (
    <ul>
        {files.map((file, i) =>
            <li key={i}>{file.name}
                <button onClick={() => (handleClick(file.id))}>Delete</button>
            </li>
        )}
    </ul>
);

Files.propTypes = {
    files: PropTypes.array.isRequired,
    handleClick: PropTypes.func.isRequired
};

export default Files

actions.js

I am wanting to trigger a request to get a new list of files from the API after the delete action is done.

export const deleteFileById = (dispatch, fileId) => {
    dispatch(deleteFile);
    return fetch(`/api/files/${fileId}`, {method : 'delete'})
        .then(dispatch(fetchFiles(dispatch)))
};

export const fetchFiles = (dispatch) => {
    dispatch(requestFiles);
    return fetch('/api/files')
        .then(response => response.json())
        .then(json => dispatch(receiveFiles(json)))
};

However I am getting the following error

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

What is the best way to implement this

An action will dispatch another action but not event handler function.

You no need to dispatch deleteFileById from component because this is a function exported in actions which will dispatch an action.

Please remove dispatch in handleClick to work.

Wrong one:

handleClick = fileId => {
    this.props.deleteFileById(dispatch(this.props.dispatch,fileId));
};

Correct one:

handleClick = fileId => {
    this.props.deleteFileById(this.props.dispatch,fileId);
};

Regarding this.props.deleteFileById is not a function.

There are many ways to access actions in your component. Below are few ways

You need to install prop-types

npm install -s prop-types

If your component is Test then set prop types as like below

import PropTypes from 'prop-types';
import React, {Component} from 'react';

class Test extends Component{
    render(){
      return(
        <div</div>
      )
    }
}

Test.propTypes = {
  deleteFileById: PropTypes.func
}

If you are using redux connect then

Without prop-types

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions';

class Test extends Component{
        render(){
          return(
            <div</div>
          )
        }
    }

export default connect(null, {...actions})(Test);

OR

With inbuilt react proptypes you no need to install prop-types separately

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions';
import {push} from 'react-router-redux';

class Test extends Component{
static get propTypes() {
    return { 
      sendContactForm: React.PropTypes.func
    }
  }
        render(){
          return(
            <div</div>
          )
        }
    }

const actionsToProps = {
      deleteFileById: actions.deleteFileById,
      push
    }

export default connect(null, actionsToProps)(Test);

Your code App.jsx should be something like below

class App extends Component {
    static propTypes = {
        files: PropTypes.array.isRequired,
        isFetching: PropTypes.bool.isRequired,
        deleteFileById : PropTypes.func,
        fetchFiles: PropTypes.func
    };

    componentDidMount() {
       this.props.fetchFiles();
    }

    handleClick = fileId => {
        this.props.deleteFileById(fileId);
    };

    render() {
        const {files, isFetching} = this.props;
        const isEmpty = files.length === 0;
        return (
            <div>
                <h1>Uploadr</h1>
                {isEmpty
                    ? (isFetching ? <h2>Loading...</h2> : <h2>No files.</h2>)
                    : <div style={{opacity: isFetching ? 0.5 : 1}}>
                        <Files files={files} handleClick={this.handleClick}/>
                    </div>
                }
            </div>
        )
    }
}

const mapStateToProps = state => {
    const {isFetching, items: files} = state.files;

    return {
        files,
        isFetching,
    }
};


export default connect(mapStateToProps)(App)

dispatch should be returned in actions but not from component to actions or vice versa

Below is sample action file for your ref.

import ajax from '../ajax';
import {Map, fromJS} from 'immutable';
import config from '../config';
import {push} from 'react-router-redux'

export const URL_PREFIX = 'http://localhost:3000/api';

export const SEND_CONTACT_FORM_REQUEST = 'SEND_CONTACT_FORM_REQUEST';
export const SEND_CONTACT_FORM_SUCCESS = 'SEND_CONTACT_FORM_SUCCESS';
export const SEND_CONTACT_FORM_ERROR = 'SEND_CONTACT_FORM_ERROR';


export function sendContactFormRequest(){
  return {
    type: SEND_CONTACT_FORM_REQUEST,
    loading: true
  }
}

export function sendContactFormSuccess(data){
  return {
    type: SEND_CONTACT_FORM_SUCCESS,
    loading: false,
    data: data
  }
}

export function sendContactFormError(errors){
  return {
    type: SEND_CONTACT_FORM_ERROR,
    loading: false,
    errors: errors
  }
}



export function sendContactForm(firstName, lastName, email, subject, message) {
  return dispatch => {
    dispatch(sendContactFormRequest());
    return ajax.post(URL_PREFIX + '/communication/contact', { firstName, lastName, email, subject, message })
      .then(res => {
        dispatch(sendContactFormSuccess(res.data))


      })
      .catch(errors => {
        dispatch(sendContactFormError(errors))
      })
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM