简体   繁体   中英

Why is the onClick event triggering twice even though there's e.preventDefault using hooks in React?

Update: The problem is directly in the reducer and has nothing to do with the onClick function nor the deleteLastItem function. I tested this with console.logs and apparently the only one repeating twice is the one directly in the reducer.

I was testing out the hooks useReducer and useContext at the same time because I thought they could work really well together but apparently, when I set a function to an onClick function, it's getting triggered twice, and it's fundamental that it doesn't because I'm deleting the last item from an array, so it deletes two items instead of one.

Here's the file where the context and the reducer hooks are stored:

import React, {createContext, useReducer} from "react";

const initialState = {
  books: [
    {name: "Book 8", autor: "Author 8", id: 8},
    {name: "Book 7", autor: "Author 7", id: 7},
    {name: "Book 6", autor: "Author 6", id: 6},
    {name: "Book 5", autor: "Author 5", id: 5},
    {name: "Book 4", autor: "Author 4", id: 4},
    {name: "Book 3", autor: "Author 3", id: 3}
  ]
}

const reducer = (state, action) => {
  switch (action.type) {
    case "DELETE":
      state.books.pop();
      console.log("hey");
      return {...state}
    default:
      return {...state}
  }
}

export const BookContext = createContext();

export const BookProvider = props => {
  const [state, dispatch] = useReducer(reducer, initialState);

  // Actions
  const deleteLastBook = () => dispatch({type: "DELETE"});

  return (
    <BookContext.Provider value={{books: state.books, deleteLastBook}}>
      {props.children}
    </BookContext.Provider>
  )
}

As you can see, the console.log("hey") , is what tells me that it's being executed twice (other than seeing the items in the screen being wiped out twice at a time)

Here's the component where the context is being brought in and where the problem occurs:

import React, {useContext} from 'react';
import {BookContext} from "../context/BookContext";

const Books = () => {

  const information = useContext(BookContext);

  const {books, deleteLastBook} = information;

  const deleteBook = e => {
    e.preventDefault();
    deleteLastBook();
  };

  return (
    <div>
      {console.log(books)}
      {books.map(book => (<h1 key={book.id}>{book.name}</h1>))}
      <button onClick={deleteBook}>Delete book</button>
    </div>
  )
}

export default Books;

And lastly, here' the app.js, I don't think it will be useful but just in case:

import React from 'react';
import './App.css';
import {BookProvider} from "./context/BookContext";
import Books from "./components/Books";

const App = () => {
  return (
    <BookProvider>
      <div className="App">
        <Books />
      </div>
    </BookProvider>
  );
}

export default App;

The solution to this was the following, I changed the reducer return statement using the filter method and it worked:

const reducer = (state, action) => {
  switch (action.type) {
    case "DELETE":
      const lastItem = state.books[state.books.length - 1];
      return {
        ...state,
        books: state.books.filter(book => book !== lastItem)
      }
    default:
      return {...state}
  }
}

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