Я новичок в реагировании и создаю приложение для ведения заметок, используя реакцию и Firebase. Данные правильно хранятся и извлекаются из Firebase и правильно отображаются в пользовательском интерфейсе.
У меня есть поле ввода на панели навигации для поиска заметки.
проблема, с которой я столкнулся:
когда я ищу конкретную заметку, она фильтруется, но когда я очищаю поле поиска, виден только отфильтрованный элемент.
Это из-за неправильного управления состоянием (если я не ошибаюсь), но я не знаю, как им правильно управлять.
Это мой компонент приложения, из которого я передаю свои заметки, полученные из Firebase, в компонент Navbar.
import './App.css';
import NavBar from './components/Navbar/NavBar';
import Header from './components/Header/Header';
import Card from './components/Card/Card';
import { useEffect, useState } from 'react';
function App() {
const [notedata, setNoteData] = useState([]);
// fetch data from firebase.
let autoFetch = async () => {
const response = await fetch('https://notes-keeper-react-default-rtdb.firebaseio.com/notes.json');
const data = await response.json();
let notesArr = [];
Object.keys(data).map((key) => {
notesArr.push(data[key]);
});
setNoteData(notesArr);
};
useEffect(() => {
autoFetch();
}, []);
console.info('Data in App comp: ', notedata);
return (
<div className='App'>
<NavBar noteData = {notedata} setNoteData = {setNoteData} />
<Header autoFetch = {autoFetch} />
<Card notes = {notedata} />
</div>
);
}
export default App;
И это мой компонент Navbar, где я фильтрую:
import { useState, useEffect } from 'react';
import classes from './NavBar.module.css';
export default function NavBar(props) {
const [filterSearch, setFilterSearch] = useState('');
const myNotes = [...props.noteData];
const filterHandler = (event) => {
let searchItem = event.target.value.toLowerCase();
setFilterSearch(searchItem);
console.info('searching for: ', searchItem);
// if (searchItem.length > 0) {
// const filterNote = props.noteData.filter((note) => note.title.toLowerCase().includes(filterSearch));
// props.setNoteData(filterNote);
// } else {
// props.setNoteData(props.noteData);
// }
const filterNote = myNotes.filter((note) => note.title.toLowerCase().includes(searchItem));
console.info('MyNotes(copied): ', myNotes, ' filterNote: ', filterNote);
props.setNoteData(searchItem.length > 0 ? filterNote : props.noteData);
};
// useEffect(() => {
// console.info('inside filter useEffect');
// const filterNote = filterSearch
// ? props.noteData.filter((note) => note.title.toLowerCase().includes(filterSearch.toLowerCase()))
// : props.noteData;
// props.setNoteData(filterNote);
// }, [filterSearch]);
return (
<div className='container'>
<nav>
<div className = {classes['nav-heading']}>
<h1>Notes</h1>
</div>
<div className = {classes['nav-search']}>
<div className = {classes['nav-controls']}>
<input
type='text'
name='search'
id='search'
placeholder='Search for Title'
value = {filterSearch}
onChange = {filterHandler}
/>
</div>
</div>
</nav>
</div>
);
}
Я также попробовал эти коды с комментариями.
Это моя консоль, когда я ищу: консоль во время поиска
Это моя консоль, когда я очищаю поле поиска: консоль после пустого поля поиска
Если вы внимательно просмотрите состояния на консоли, вы увидите проблему.
🤔 А знаете ли вы, что...
JavaScript поддерживает объектно-ориентированное программирование.
Вы напрямую обновляете состояние notedata
при фильтрации. Поэтому в следующий раз, когда вы измените searchFilter
, он отфильтрует последний существующий notedata
.
NavBar
отвечает только за обновление состояния filterSearch
. App
отвечает за получение и фильтрацию заметок.
Переместите состояние filterSearch
в состояние App
. В теле компонента App
фильтруйте notedata
при каждом изменении filterSearch
(или notedata
).
function App() {
const [notedata, setNoteData] = useState([]);
const [filterSearch, setFilterSearch] = useState('');
useEffect(() => { // the useEffect calls the api
// fetch data from firebase.
const autoFetch = async () => {
const response = await fetch('https://notes-keeper-react-default-rtdb.firebaseio.com/notes.json');
const data = await response.json();
setNoteData(Object.values(data)); // convert data to an array
};
autoFetch();
}, []);
// filter the notes when the notesData or the search changes
const filteredNotes = useMemo(() =>
filterSearch === ''
? notedata // don't filter if empty
: notedata.filter(note =>
note.title.toLowerCase().includes(filterSearch)
)
, [notedata, filterSearch])
return (
<div className='App'>
<NavBar
filterSearch = {filterSearch}
setFilterSearch = {setFilterSearch}
/>
<Header />
<Card notes = {filteredNotes} />
</div>
);
}
export default function NavBar({ filterSearch, setFilterSearch }) {
const filterHandler = event => {
const searchItem = event.target.value.toLowerCase();
setFilterSearch(searchItem);
};
return (
<div className='container'>
<nav>
<div className = {classes['nav-heading']}>
<h1>Notes</h1>
</div>
<div className = {classes['nav-search']}>
<div className = {classes['nav-controls']}>
<input
type='text'
name='search'
id='search'
placeholder='Search for Title'
value = {filterSearch}
onChange = {filterHandler}
/>
</div>
</div>
</nav>
</div>
);
}