0

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;
gxp2
  • 149
  • 2
  • 11
  • Try with stopPropagation: https://stackoverflow.com/questions/36316846/react-onclick-and-preventdefault-link-refresh-redirect – gtamborero Sep 06 '20 at 22:17
  • That doesn't either @gtamborero – gxp2 Sep 06 '20 at 22:21
  • Not sure if you have a form around this button by maybe just add `type="button"` to the button to see if it doesn't submit a form somewhere? – Dominik Sep 06 '20 at 22:23
  • I don't and that doesn't work either :( @Dominik – gxp2 Sep 06 '20 at 22:24
  • When you add a console.log into your function `deleteBook` will that also console log it twice? – Dominik Sep 06 '20 at 22:25
  • Bingo!, it only does once. That means the problem is in the reducer. Really good catch, do you know how you could fix it? @Dominik – gxp2 Sep 06 '20 at 22:30
  • Update: the problem is directly in the reducer and not in the function that executes it in the context (deleteLastBook) because I exported the reducer directly (dispatch function) in the context to the component and it does the same thing. – gxp2 Sep 06 '20 at 22:33
  • I'm not using redux or `useReducer` at all in any of our projects so I don't know what it could be. Make sure you share your findings and add them as an answer if no one else answers this question – Dominik Sep 06 '20 at 22:37
  • @Dominik , okay, just added it, thanks for the help and if you come up with a solution then don't forget to tell me :) – gxp2 Sep 06 '20 at 22:41

1 Answers1

0

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}
  }
}
gxp2
  • 149
  • 2
  • 11