Ответ 1
В вашем редукторе состояние должно быть массивом, но во время удаления вы вернули объект:
case DELETE_BOOK:
let afterDelete = state.filter(book => {
return book._id !== action.payload._id
});
return afterDelete;
Вначале это примерное приложение работало правильно. Я мог видеть данные, которые я вводил на странице браузера и в базе данных. В настоящее время я вижу данные только через базу данных, браузер не показывает данные и не получает эту ошибку дополнительно: "Неверная попытка распространения неистребимого экземпляра".
Существует пример кода:
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;
BookListElement.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;
Я проверил код несколько раз и прочитал более старую запись об этой проблеме, но я не нашел решения в качестве младшего. Было бы здорово, если бы вы сказали, что я пропустил.
EDIT: Добавлен файл view.js как bookList.js.
В вашем редукторе состояние должно быть массивом, но во время удаления вы вернули объект:
case DELETE_BOOK:
let afterDelete = state.filter(book => {
return book._id !== action.payload._id
});
return afterDelete;
Эта ошибка возникает, когда вы пытаетесь скопировать неопределенный массив в ваш редуктор. например:
const newList = [...state.someList];
В этом случае 'someList' не определен или не является массивом. убедитесь, что ваше написание или вы используете правильный массив.
Я думаю, что вы должны вернуть ответ извлечения в состояние редуктора, и вы можете отфильтровать удаленные книги в одну строку. может быть, это сработает.
//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;
Я тоже получил эту ошибку. В моем случае ошибка состояла в том, что элемент, который я пытался клонировать, был фактически объектом, а не массивом!
Это то, что я в итоге сделал вместо этого:
var clone = {...this.state.myObj};
(вместо)
var clone = [...this.state.myObj];
Я знаю, действительно очевидно, но мы все начинаем где-то, верно?