【问题标题】:React/Redux - I delete an item out of 6 total, when re-render happens it appends the old list(6) onto the new list(5) now I have 11 items?React/Redux - 我从 6 个项目中删除了一个项目,当重新渲染发生时,它会将旧列表(6)附加到新列表(5)上,现在我有 11 个项目?
【发布时间】:2018-03-27 05:45:50
【问题描述】:

最初我想要做的是将 deleteItem 事件附加到每个书项。我有一个动作 'DELETE_BOOK' 接受书目的 id,然后在减速器中它返回没有我指定要删除的书目的书目列表。该项目被删除,但由于某种原因,它将旧列表(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

【问题讨论】:

    标签: javascript reactjs redux react-redux jsx


    【解决方案1】:

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

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

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

    【讨论】:

    • 是的,我正在关注这本书Reducer 代码来自的教程,这就是我使用 findIndex 方式的原因。但我试图在前端稍微偏离教程,因为教程使用下拉菜单删除它们并提交,但我想在按钮级别附加删除功能。但让我按你的方式来吧。
    • 你能把教程的链接发上来吗?
    • 是的!您的解决方案奏效了!我唯一需要做的就是从有效载荷上取下“_id”。 `
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 2018-04-18
    • 2020-11-13
    • 1970-01-01
    • 2020-10-30
    • 2018-04-10
    • 1970-01-01
    相关资源
    最近更新 更多