React/Redux - 当重新渲染发生时,它会将旧列表(6)追加到新列表(5)中,现在我有11个项目?

问题描述:

最初我想要做的是将deleteItem事件附加到每个书籍项目。我有一个操作'DELETE_BOOK',它接受书籍项目的ID,然后在缩减器中返回没有我指定要删除的书籍项目的书籍列表。该项目被删除,但由于某种原因,它将旧列表(6个项目)追加到新列表(删除后的5个项目),现在我结束了11个项目。React/Redux - 当重新渲染发生时,它会将旧列表(6)追加到新列表(5)中,现在我有11个项目?

Entire project source code

//book-list.js  
"use strict"  
import React from 'react'; 
import {connect} from 'react-redux'; 
import {bindActionCreators} from 'redux'; 
import {getBooks, deleteBook} from '../../actions/booksActions'; 
import BookItem from './book-item'; 
import BookForm from './book-form'; 
import Cart from './cart'; 

class BookList extends React.Component { 

    constructor(props){ 
     super(props); 
     this.deleteBookItem = this.deleteBookItem.bind(this); 
    } 

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

    deleteBookItem(_id){ 
     this.props.deleteBook(_id); 
    } 

    render(){   
     const bookList = this.props.books.map(function(book){ 
      return (
       <BookItem 
        key={book._id} 
        _id={book._id} 
        title={book.title} 
        description={book.description} 
        price={book.price} 
        deleteBookItem={this.deleteBookItem} 
       /> 
      ) 
     }, this); 

     return(
      <div> 
       <div className="page-header"> 
        <h1 className="text-center">The React BookStore</h1> 
       </div> 
       { this.props.msg && 
        <div className="alert alert-info text-center" 
        role="alert">{this.props.msg}</div> 
       }     
       <Cart />     
       <div className="row"> 
        <div className="col-xs-12 col-sm-8"> 
         <div className="row"> 
          {bookList} 
         </div> 
        </div> 
        <div className="col-xs-12 col-sm-4"> 
         <BookForm /> 
        </div> 
       </div>        
      </div> 
     ) 
    } 
} 

//just return the data from the store 
function mapStateToProps(state){ 
    return {   
     books: state.books.books, 
     msg: state.books.msg 
    } 
} 

function mapDispatchToProps(dispatch){ 
    return bindActionCreators({ 
     getBooks: getBooks, 
     deleteBook: deleteBook 
    } 
    , dispatch); 
} 
//connects component to the store 
export default connect(mapStateToProps, mapDispatchToProps)(BookList); 

---------------------------------------------------------------------------- 

//book-item.js 
import React from 'react'; 
import {connect} from 'react-redux'; 
import {bindActionCreators} from 'redux'; 
import {addToCart, updateCart} from '../../actions/cartActions'; 

class BookItem extends React.Component{ 

    constructor(props){ 
     super(props); 
     this.deleteBookItem = this.deleteBookItem.bind(this); 
    } 

    deleteBookItem(){ 
     const index = this.props._id; 
     this.props.deleteBookItem(index); 
    } 

    handleCart =() => { 
     const book = [...this.props.cart, { 
      _id: this.props._id, 
      title: this.props.title, 
      description: this.props.description, 
      price: this.props.price, 
      qty: 1 
     }]; 
     if(this.props.cart.length > 0){ 
      let _id = this.props._id; 
      let cartIndex = this.props.cart.findIndex(function(cart){ 
       return cart._id === _id; 
      }); 
      if(cartIndex === -1){ 
       this.props.addToCart(book); 
      } 
      else{ 
       this.props.updateCart(_id, 1); 
      } 
     } 
     else { 
      this.props.addToCart(book); 
     }   
    } 

    render(){ 
     return(
      <div className="col-xs-12 col-md-6" key={this.props._id}> 
       <div className="well"> 
        <h2 className="text-center">{this.props.title}</h2> 
        <h2 className="text-center">{this.props.description} 
        </h2> 
        <h2 className="text-center">{this.props.price}</h2> 
        <button className="btn btn-success btn-block" onClick= 
        {this.handleCart}> 
         <i className="glyphicon glyphicon-shopping-cart"> 
         </i> 
         <span> Add To Cart</span> 
        </button> 
        <button className="btn btn-danger btn-block" onClick= 
        {this.deleteBookItem}> 
         <i className="glyphicon glyphicon-trash"></i> 
         <span> Delete Book</span> 
        </button> 
       </div>      
      </div> 
     ) 
    } 
} 

function mapStateToProps(state){ 
    return { 
     cart: state.cart.cart 
    } 
} 

function mapDispatchToProps(dispatch){ 
    return bindActionCreators(
     { 
      addToCart: addToCart, 
      updateCart: updateCart, 
     } 
     , dispatch); 
} 
export default connect(mapStateToProps, mapDispatchToProps)(BookItem); 

--------------------------------------------------------------------------- 

//bookActions.js 
"use strict" 

export function getBooks(){ 
    return { 
     type: 'GET_BOOKS' 
    } 
} 

export function postBook(book){ 
    return { 
     type: 'POST_BOOK', 
     payload: book 
    } 
} 

export function deleteBook(_id){ 
    return { 
     type: 'DELETE_BOOK', 
     payload: _id 
    } 
} 

export function updateBook(book){ 
    return { 
     type: 'UPDATE_BOOK', 
     payload: book 
    } 
} 

--------------------------------------------------------------------------- 
//booksReducers.js 
"use strict" 
//BOOKS REDUCERS 
let defaultBooks = [ 
    { 
    _id: 1, 
    title: 'Book 1', 
    description: 'Book 1 Description', 
    price: 19.99 
    }, 
    { 
     _id: 2, 
     title: 'Book 2', 
     description: 'Book 2 Description', 
     price: 29.99 
    }, 
    { 
    _id: 3, 
    title: 'Book 3', 
    description: 'Book 3 Description', 
    price: 39.99 
    }, 
    { 
     _id: 4, 
     title: 'Book 4', 
     description: 'Book 4 Description', 
     price: 49.99 
    }, 
    { 
    _id: 5, 
    title: 'Book 5', 
    description: 'Book 5 Description', 
    price: 59.99 
    }, 
    { 
     _id: 6, 
     title: 'Book 6', 
     description: 'Book 6 Description', 
     price: 69.99 
    } 
]; 
export function booksReducers(state = { books: defaultBooks }, action){ 

    switch(action.type){ 
    case "GET_BOOKS": 
     return {...state, books:[...state.books]} 
     break; 
    case "POST_BOOK": 
     return {...state, books:[...state.books, ...action.payload], 
     msg:'Saved! Click to continue', style:'success', 
     validation:'success'} 
     break; 
    case "POST_BOOK_REJECTED": 
     return {...state, msg:'Please, try again', style:'danger', 
     validation:'error'} 
     break; 
    case "RESET_BUTTON": 
     return {...state, msg:null, style:'primary', validation:null} 
     break; 
    case "DELETE_BOOK": 
     // Create a copy of the current array of books 
     const currentBookToDelete = [...state.books]; 
     // Determine at which index in books array is the book to be deleted 
     const indexToDelete = currentBookToDelete.findIndex(function(book){ 
      return book._id === action.payload._id; 
     }); 
     //use slice to remove the book at the specified index 
     return {books: [...currentBookToDelete.slice(0, indexToDelete), 
     ...currentBookToDelete.slice(indexToDelete + 1)]} 
     break; 

    case "UPDATE_BOOK": 
     // Create a copy of the current array of books 
     const currentBookToUpdate = [...state.books] 
     // Determine at which index in books array is the book to be deleted 
     const indexToUpdate = currentBookToUpdate.findIndex(
      function(book){ 
      return book._id === action.payload._id; 
      } 
     ) 
     // Create a new book object with the new values and with the same 
     array index of the item we want to replace. To achieve this we will 
     use ...spread but we could use concat methos too 
     const newBookToUpdate = { 
      ...currentBookToUpdate[indexToUpdate], 
      title: action.payload.title 
     } 
     // Log has the purpose to show you how newBookToUpdate looks like 
     console.log("what is it newBookToUpdate", newBookToUpdate); 
     //use slice to remove the book at the specified index, replace with 
     the new object and concatenate witht he rest of items in the array 
     return { 
      books: [...currentBookToUpdate.slice(0, indexToUpdate), 
      newBookToUpdate, ...currentBookToUpdate.slice(indexToUpdate + 1)] 
     } 
     break; 
     default: 
     break; 
    } 
    return state 
} 

[enter code here][1] 


[1]: https://i.stack.imgur.com/JCTyr.png 

为什么不使用过滤功能?检查它是否与此一起工作:

case "DELETE_BOOK": 
    const bookId = action.payload._id; 
    return {books: state.books.filter(book => book._id !== bookId} 

而且你不需要在switch语句返回后断开。

+0

是的,我正在关注这本bookReducer代码的教程,这就是我使用findIndex方法的原因。但是我试图在前端稍微偏离教程,因为教程使用下拉菜单和提交来删除它们,但我想在按钮级别附加删除功能。但是,让我以你的方式。 –

+0

您能否将链接发布到教程中? –

+0

[Full stack Universal React with Redux,Node js and MongoDB](https://www.udemy.com/full-stack-universal-react-with-redux-express-and-mongodb/?src=sac&kw=full) –