React, problem with my Todo list app, my search engine only finds notes on the page I'm on

Viewed 71

Hello I am making an application to practice React, my notes app has a pagination which works perfectly, the problem is in the search engine, which only looks for notes from the page I'm on, for example, if I'm on page 2 and I look for a note on page 2, it shows it, however if the note is on a different page, it doesn't show it, it doesn't find it.

I know where the problem is but I'm not sure how to solve it, I'm a bit new to React and I was asking for your help.

I was able to do my pagination with the package react-paginate here is the documentation https://www.npmjs.com/package/react-paginate

My code:

Component App.js

import { useState, useEffect } from "react";
import { nanoid } from 'nanoid';
import './App.css';
import Search from "./components/Search";
import Header from "./components/Header";
import Pagination from "./components/Pagination";

const App = () => {
  const [notes, setNotes] = useState([]);
  const [searchText, setSearchText] = useState('');
  const [darkMode, setDarkMode] = useState(false); 

  const [showNote, setShowNote] = useState(true); //eslint-disable-line
  useEffect(() => {
    const saveNotes = JSON.parse(localStorage.getItem('notes-data'));
    if (saveNotes){
      setNotes(saveNotes);
    }
    
  }, []);
  useEffect(() => {
    localStorage.setItem('notes-data', JSON.stringify(notes))
  },[notes])

  const addNote = (inputText, text) => { 
    const date = new Date();
    const newNote = {
      id: nanoid(),
      title: inputText,
      text: text,
      date: date.toLocaleString()
      
    }
    const newNotes = [newNote, ...notes];
    setNotes(newNotes)
  }
  const deleteNote = (id) => {
    var response = window.confirm("Are you sure?");
    if (response){
      const notesUpdated = notes.filter((note) => note.id !== id)
      setNotes(notesUpdated);
    }
  }
  return (
    <div className={darkMode ? 'dark-mode' : ''}>
      <div className="container">
        <Header 
          handleToggleTheme={setDarkMode}
        />
        <Search 
          handleSearchNote={setSearchText}
          setShowNote={setShowNote}
        />
        <Pagination 
          data={notes} 
          handleAddNote={addNote}
          handleDeleteNote={deleteNote}
          searchText={searchText}
        />
      </div>
    </div>
  )
}

export default App;

Component Pagination.js

import React, { useEffect, useState } from 'react'
import ReactPaginate from 'react-paginate';
import '../styles/Pagination.css';
import NoteList from './NoteList';
import { MdSkipPrevious, MdSkipNext } from 'react-icons/md';


const Pagination = (props) => {
  const { data, searchText, handleAddNote, handleDeleteNote } = props;
    
  // We start with an empty list of items.
  const [currentItems, setCurrentItems] = useState([]);
  const [pageCount, setPageCount] = useState(0);
  // Here we use item offsets; we could also use page offsets
  // following the API or data you're working with.
  const [itemOffset, setItemOffset] = useState(0);
  const itemsPerPage = 9;

  useEffect(() => {
    // Fetch items from another resources.
    const endOffset = itemOffset + itemsPerPage;
    console.log(`Loading items from ${itemOffset} to ${endOffset}`);
    setCurrentItems(data.slice(itemOffset, endOffset));
    setPageCount(Math.ceil(data.length / itemsPerPage));
  }, [itemOffset, itemsPerPage, data]);

  // Invoke when user click to request another page.
  const handlePageClick = (event) => {
  const newOffset = (event.selected * itemsPerPage) % data.length;
    console.log(
    `User requested page number ${event.selected}, which is offset ${newOffset}`
    );
    setItemOffset(newOffset);
  };

  return (
    <>
      <NoteList 
       notes={currentItems.filter((noteText) => 
          noteText.title.toLowerCase().includes(searchText)
        )}
        handleAddNote={handleAddNote}
        handleDeleteNote={handleDeleteNote}
      />
      <div className="pagination-wrapper">

      <ReactPaginate
        breakLabel="..."
        nextLabel={<MdSkipNext 
          className='icons'
        />}
        onPageChange={handlePageClick}
        pageRangeDisplayed={3}
        pageCount={pageCount}
        previousLabel={<MdSkipPrevious 
          className='icons'
        />}
        renderOnZeroPageCount={null}
        containerClassName="pagination"
        pageLinkClassName="page-num"
        previousLinkClassName="page-num"
        nextLinkClassName="page-num"
        activeLinkClassName="activee boxx"
      />
      </div>
    </>
  );
}

export default Pagination;


Component NoteList.js

import React from 'react'
import Note from './Note'
import '../styles/NoteList.css'
import AddNote from './AddNote'

const NoteList = ({ notes, handleAddNote, handleDeleteNote }) => {
  return (
    <>
      <div className="add-notes-wrapper">
        <AddNote 
          handleAddNote={handleAddNote}     
        />
      </div>
      <div className='notes-list'>
        {notes.map((note => 
          <Note 
            key={note.id}
            id={note.id}
            title={note.title}
            text={note.text}
            date={note.date}
            handleDeleteNote={handleDeleteNote}
         />
        ))}     
      </div>
    </>
  )
}

export default NoteList;

Component Search.js

//import React, { useState } from 'react'
import {MdSearch, MdAdd} from 'react-icons/md'
import '../styles/Search.css'


const Search = ({ handleSearchNote, setShowNote }) => {
  const handleShowAddNote = () => {
    if (setShowNote){ 
      let addNote = document.querySelector('.new');
      addNote.classList.add('wobble-horizontal-top')
      addNote.style.display='flex';
      document.querySelector('.notes-list').style.display='none';
      document.querySelector('.pagination').style.display='none';
    }
  }
  return (
    <div className='search'>
      <div className="input-wrapper">
        <MdSearch 
          className='icon search-icon'
        /> 
        <input 
          type="text" 
          placeholder='What note are you looking for?'
          onChange={(event) => handleSearchNote(event.target.value) }
        />
      </div>
      <div className="btn-wrapper-search">
        <button 
          className='btn-addNote'
          onClick={handleShowAddNote}>
          Nueva Nota
        </button>
        <MdAdd 
          className='icon add-icon'
        />
      </div>
    </div>
  )
}

export default Search

The problem is in the component Pagination.js because I'm filtering the notes on each page with the currentItems variable, if I did it with the data variable it would work, but then it would show all the notes, and I don't want that, I currently want to show 9 notes per page.

greetings and thanks in advance.

Edit:

@Newbie I'm doing what you said, but I don't know if you mean this, in my Pagination.js component I did:

useEffect(() => {
    const filterNotes=data.filter((noteText) => 
      noteText.title.toLowerCase().includes(searchText)
    )
    setItemOffset(0);
  }, [data, searchText])

It doesn't work, do I have to pass a prop to my components additionally? greetings.

2 Answers

Search and pagination do not play well together, one of the common solutions is to jump to page 1 each time the filter term changes.

So use an useEffect on searchText to filter data and reset itemOffset to 0, then redo pagination as if the data changed.

The user will jump to page 1 at each keystroke of the search, then he can navigate pages (if there are more than one). This will lead to a less confusing UX.

Related