React - Redux 应用程序:“Invalid attempt to spread non-iterable instance”问题

30

一开始,那个示例应用程序能够正常工作。我可以通过浏览器页面和数据库查看输入的数据。现在,我只能通过数据库查看数据,浏览器不显示数据,并且还出现了这个错误:"Invalid attempt to spread non-iterable instance"。

以下是示例代码:

projectActions.js

import {FETCH_BOOK, CREATE_BOOK, DELETE_BOOK} from '../actions/projectTypes';
import axios from 'axios';

const apiUrl = 'http://api/books';

export const createBookSuccess =  (data) => {
  return {
    type: CREATE_BOOK,
    payload: {
      _id: data._id,
      author: data.author,
      publication: data.publication,
      publisher: data.publisher
    }
  }
};

export const deleteBookSuccess = _id => {
  return {
    type: DELETE_BOOK,
    payload: {
      _id
    }
  }
};

export const fetchBook = (books) => {
  return {
    type: FETCH_BOOK,
    books
  }
};

export const createBook = ({ author, publication, publisher }) => {
  return (dispatch) => {
    return axios.post(`${apiUrl}`, {author, publication, publisher})
      .then(response => {
        dispatch(createBookSuccess(response.data))
      })
      .catch(error => {
        throw(error);
      });
  };
};

export const deleteBook = _id => {
  return (dispatch) => {
    return axios.delete(`${apiUrl}/${_id}`)
      .then(response => {
        dispatch(deleteBookSuccess(response.data))
      })
      .catch(error => {
        throw(error);
      });
  };
};

export const fetchAllBooks = () => {
  return (dispatch) => {
    return axios.get(apiUrl)
      .then(response => {
        dispatch(fetchBook(response.data))
      })
      .catch(error => {
        throw(error);
      });
  };
};

projectReducer.js

import {CREATE_BOOK, DELETE_BOOK, FETCH_BOOK} from '../actions/projectTypes';

const projectReducer = (state = [], action) => {
  switch (action.types) {
    case CREATE_BOOK:
      return [...state, action.payload];
    case DELETE_BOOK:
      let afterDelete = state.filter(book => {
        return book._id !== action.payload._id
      });
      return {
        ...state,
        state: afterDelete
      }
    case FETCH_BOOK:
      return action.books;
    default:
      return state;
  }
}

export default projectReducer;

rootReducer.js

import books from './projectReducer';
import {combineReducers} from 'redux';

const rootReducer = combineReducers({
    books: books
});

export default rootReducer;

书单元素.js

import React from 'react';
import {connect} from 'react-redux';
import BookList from '../components/bookList';
import {deleteBook} from '../store/actions/projectActions';

const BookListElement= ({books, deleteBook}) => {
  if(!books.length) {
    return (
      <div>
        No Books
      </div>
    )
  }
  return (
    <div>
      {books.map(book => {
        return (
          <BookList book={book} deleteBook={deleteBook} key={book._id} />
        );
      })}
    </div>
  );
}

const mapStateToProps = state => {
  return {
    books: state.books
  };
};

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

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

bookList.js

import React from 'react';

const styles = {
  borderBottom: '2px solid #eee',
  background: '#fafafa',
  margin: '.75rem auto',
  padding: '.6rem 1rem',
  maxWidth: '500px',
  borderRadius: '7px'
};

const BookList =  ({ book: { author, publication, publisher, _id }, deleteBook }) => {
  return (
    <div style={styles} key={_id}>
      <h2>{author}</h2>
      <p>{publication}</p>
      <p>{publisher}</p>
      <button className="btn waves-effect waves-light" type="submit" name="action" onClick={() => {deleteBook(_id)}}>
        <i className="large material-icons">delete_forever</i>
      </button>
    </div>
  );
};

export default BookList;

bookCreate.js

import React, {Component} from 'react';

class BookCreate extends Component {
  state= {
    author: '',
    publication: '',
    publisher: ''
  }

  handleChange = (e) => {
    this.setState({
      [e.target.id]: e.target.value
    });
  }

  handleSubmit = (e) => {
    e.preventDefault();
    this.props.createBook(this.state)
  }

  render() {
    return (
      <div className="container">
        <form onSubmit={this.handleSubmit} className="white" autoComplete="off">
          <h5 className="grey-text text-darken-3">Create New Book</h5>
          <div className="input-field">
            <label htmlFor="author">Author</label>
            <input type="text" id="author" onChange={this.handleChange}/>
          </div>

          <div className="input-field">
            <label htmlFor="publication">Publication</label>
            <input type="text" id="publication" onChange={this.handleChange}/>
          </div>

          <div className="input-field">
            <label htmlFor="publisher">Publisher</label>
            <input type="text" id="publisher" onChange={this.handleChange}/>
          </div>

          <div className="input-field">
            <button className="btn pink lighten-1 z-depth-0">Create</button>
          </div>
        </form>
      </div>
    )
  }
}

export default BookCreate;

我多次检查了代码并阅读了一篇关于这个问题的旧帖子,但是作为一名初级开发人员,我没有找到任何解决方案。如果您能告诉我错过了什么,那就太好了。

编辑:将view.js文件添加为bookList.js。


你的错误在哪一行? - Damian Peralta
@DamianPeralta 我再次运行代码,现在没有任何错误,但是数据仍然无法在浏览器上显示。不过,我可以通过数据库看到数据。 - gilgamesh
4个回答

21

在您的reducer中,状态应该是一个数组,但是在删除时,您返回了一个对象:

case DELETE_BOOK:
  let afterDelete = state.filter(book => {
    return book._id !== action.payload._id
  });
  return afterDelete;

仍然无法正常工作,并显示“没有图书”作为结果。 - gilgamesh

20

在你的 reducer 中尝试复制一个未定义的数组时,会出现此错误。

const newList = [...state.someList];

在这种情况下,'someList' 未定义或不是一个数组。确保你的拼写正确,或者你正在使用一个正确的数组。

1
一个简单的解决方法:const newList = state.someList ? [...state.someList] : []; - Caleb

15

虽然这不是必须回答的问题,但或许这会帮助到某些人,他们遇到了这个错误并在 Stack Overflow 上碰巧看到了这篇文章。

我也遇到了这个错误。在我的情况下,错误是因为我尝试克隆的元素实际上是一个对象而不是数组!

于是我最终做的是:

var clone = {...this.state.myObj};

(而不是)

var clone = [...this.state.myObj];

我知道,这很显而易见,但我们都是从某个地方开始的,对吧?

1
或者你有一个未定义的数组! - Omar bakhsh

0

我认为你应该将fetch响应返回到reducer状态中,然后可以在一行中过滤掉已删除的书籍。也许这样会起作用。

//projectReducer.js
import {CREATE_BOOK, DELETE_BOOK, FETCH_BOOK} from '../actions/projectTypes';

const projectReducer = (state = [], action) => {
  switch (action.types) {
    case CREATE_BOOK:
      return [...state, action.payload];
    case DELETE_BOOK:
      return state = state.filter(book => book._id !== action.payload._id)
    case FETCH_BOOK:
      return state = action.books;
    default:
      return state;
  }
}

export default projectReducer;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接