Checkbox filtering on object property - react - javascript

So i have some dummy data and want to filter through array and display objects that have that specific status.
Currently when i click on some checkbox it filters objects with clicked status correctly but then from what i understand - filtered data gets saved to invoiceList state so unchecking it has zero sense, because next filtering is based on that filtered objects previously.
I also want to combine checked checkboxes so it filters objects with both e.g. "paid" and "pending" statutes.
How to do all of these filerings properly?
const {useState, useEffect} = React;
const DATA = [
{
name: "invoice1",
status: "paid"
},
{
name: "invoice2",
status: "paid"
},
{
name: "invoice3",
status: "pending"
},
{
name: "invoice4",
status: "draft"
}
];
const App = () => {
const [invoiceList, setInvoiceList] = useState([]);
useEffect(() => {
setInvoiceList(DATA);
}, []);
const statuses = ["draft", "pending", "paid"];
const [checkedState, setCheckedState] = useState(
new Array(statuses.length).fill(false)
);
const handleCheckboxChange = (position, status) => {
const updatedCheckedState = checkedState.map((item, index) =>
index === position ? !item : item
);
setCheckedState(updatedCheckedState);
//THIS
////////////////////////////////////////////
const filteredList = invoiceList.filter(
(invoice) => invoice.status === status
);
setInvoiceList(filteredList);
};
return (
<div>
<div>
{statuses.map((status, index) => (
<label key={index}>
<input
type="checkbox"
checked={checkedState[index]}
onChange={(e) => handleCheckboxChange(index, status)}
/>
<span>{status}</span>
</label>
))}
</div>
<ul>
{invoiceList.map((invoice,index) => {
return <li key={index}>{invoice.name}</li>;
})}
</ul>
</div>
);
}
const root = ReactDOM.createRoot(
document.getElementById("root")
).render(<App/>);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

Instead of saving the state using setInvoiceList, you may filter the list using useMemo hook.
const {useState, useEffect, useMemo} = React;
const DATA = [
{
name: "invoice1",
status: "paid"
},
{
name: "invoice2",
status: "paid"
},
{
name: "invoice3",
status: "pending"
},
{
name: "invoice4",
status: "draft"
}
];
const App = () => {
const [invoiceList, setInvoiceList] = useState([]);
useEffect(() => {
setInvoiceList(DATA);
}, []);
const statuses = ["draft", "pending", "paid"];
// keep track of the selected/active status
const [activeStatus, setActiveStatus] = useState();
const filteredInvoices = useMemo(() => {
// if no active status, return all invoices
if (!activeStatus) {
return invoiceList;
}
// otherwise, filter invoices by active status
return invoiceList.filter(invoice => invoice.status === activeStatus);
},[activeStatus, invoiceList]);
return (
<div>
<div>
{statuses.map((status, index) => (
<label key={index}>
<input
type="checkbox"
checked={statuses[index] === activeStatus}
onChange={(e) => setActiveStatus(status === activeStatus ? undefined : status)}
/>
<span>{status}</span>
</label>
))}
</div>
<ul>
{filteredInvoices.map((invoice,index) => {
return <li key={index}>{invoice.name}</li>;
})}
</ul>
</div>
);
}
const root = ReactDOM.createRoot(
document.getElementById("root")
).render(<App/>);
For multiple status support, you can use the following
const {useState, useEffect, useMemo} = React;
const DATA = [
{
name: "invoice1",
status: "paid"
},
{
name: "invoice2",
status: "paid"
},
{
name: "invoice3",
status: "pending"
},
{
name: "invoice4",
status: "draft"
}
];
const App = () => {
const [invoiceList, setInvoiceList] = useState([]);
useEffect(() => {
setInvoiceList(DATA);
}, []);
const statuses = ["draft", "pending", "paid"];
// keep track of the selected/active statuses
const [activeStatuses, setActiveStatuses] = useState([]);
// toggle the status
const toggleStatus = (status) => {
if (activeStatuses.includes(status)) {
setActiveStatuses(activeStatuses.filter((s) => s !== status));
} else {
setActiveStatuses([...activeStatuses, status]);
}
};
// filter the invoices based on the active statuses
const filteredInvoices = useMemo(() => {
// if there are no active statuses, return the original list
if (activeStatuses.length === 0) {
return invoiceList;
}
// otherwise, filter the list
return invoiceList.filter(invoice => activeStatuses.includes(invoice.status));
},[activeStatuses, invoiceList]);
return (
<div>
<div>
{statuses.map((status, index) => (
<label key={index}>
<input
type="checkbox"
checked={activeStatuses.includes(status)}
onChange={(e) => toggleStatus(status)}
/>
<span>{status}</span>
</label>
))}
</div>
<ul>
{filteredInvoices.map((invoice,index) => {
return <li key={index}>{invoice.name}</li>;
})}
</ul>
</div>
);
}
const root = ReactDOM.createRoot(
document.getElementById("root")
).render(<App/>);

Related

How to avoid prop drilling ? / How to use useContext?

I'm working on a React Notes Application and my App.js contains all the necessary functions props which are passed down to several components.
As a result I'm doing prop drilling a lot where I'm passing down around 10-20 props/functions in the components where it isn't needed.
I tried using useContext Hook but I guess it doesn't work with callback functions in the value parameter.
App.js
const App = () => {
const [ notes, setNotes ] = useState([]);
const [ category, setCategory ] = useState(['Notes']);
const [ searchText, setSearchText ] = useState('');
const [ alert, setAlert ] = useState({
show:false,
msg:'',
type:''
});
const [isEditing, setIsEditing] = useState(false);
const [editId, setEditId] = useState(null);
useEffect(()=>{
keepTheme();
})
// retreive saved notes
useEffect(()=>{
const savedNotes = JSON.parse(localStorage.getItem('react-notes-data'));
if (savedNotes) {
setNotes(savedNotes)
}
}, []);
// save notes to local storage
useEffect(() => {
localStorage.setItem('react-notes-data', JSON.stringify(notes))
setNotesCopy([...notes]);
}, [notes]);
// save button will add new note
const addNote = (text) => {
const date = new Date();
const newNote = {
id: nanoid(),
text: text,
date: date.toLocaleDateString(),
category: category,
}
const newNotes = [...notes, newNote]
setNotes(newNotes);
}
const deleteNote = (id) => {
showAlert(true, 'Note deleted', 'warning');
const newNotes = notes.filter(note => note.id !== id);
setNotes(newNotes);
}
// hardcoded values for categories
const allCategories = ['Notes', 'Misc', 'Todo', 'Lecture Notes', 'Recipe'];
// copy notes for filtering through
const [notesCopy, setNotesCopy] = useState([...notes]);
const handleSidebar = (category) => {
setNotesCopy(category==='Notes'?[...notes]:
notes.filter(note=>note.category===category));
}
// function to call alert
const showAlert = (show=false, msg='', type='') => {
setAlert({show, msg, type});
}
return (
<div>
<div className="container">
<Sidebar
allCategories={allCategories}
handleSidebar={handleSidebar}
notesCopy={notesCopy}
key={notes.id}
/>
<Header notes={notes} alert={alert} removeAlert={showAlert} />
<Search handleSearchNote={setSearchText} />
<NotesList
notesCopy={notesCopy.filter(note=>
note.text.toLowerCase().includes(searchText) ||
note.category.toString().toLowerCase().includes(searchText)
)}
handleAddNote={addNote}
deleteNote={deleteNote}
category={category}
setCategory={setCategory}
allCategories={allCategories}
showAlert={showAlert}
notes={notes}
setNotes={setNotes}
editId={editId}
setEditId={setEditId}
isEditing={isEditing}
setIsEditing={setIsEditing}
/>
</div>
</div>
)
}
NotesList.js
const NotesList = (
{ notesCopy, handleAddNote, deleteNote, category, setCategory, showHideClassName, allCategories, showAlert, isEditing, setIsEditing, notes, setNotes, editId, setEditId }
) => {
const [ noteText, setNoteText ] = useState('');
const textareaRef = useRef();
// function to set edit notes
const editItem = (id) => {
const specificItem = notes.find(note=>note.id === id);
setNoteText(specificItem.text);
setIsEditing(true);
setEditId(id);
textareaRef.current.focus();
}
return (
<div key={allCategories} className="notes-list">
{notesCopy.map(note => {
return (
<Note
key={note.id}
{...note}
deleteNote={deleteNote}
category={note.category}
isEditing={isEditing}
editId={editId}
editItem={editItem}
/>)
})}
<AddNote
handleAddNote={handleAddNote}
category={category}
setCategory={setCategory}
showHideClassName={showHideClassName}
allCategories={allCategories}
showAlert={showAlert}
isEditing={isEditing}
setIsEditing={setIsEditing}
notes={notes}
setNotes={setNotes}
editId={editId}
setEditId={setEditId}
noteText={noteText}
setNoteText={setNoteText}
textareaRef={textareaRef}
/>
</div>
)
}
AddNote.js
const AddNote = ({ notes, setNotes, handleAddNote, category, setCategory, showHideClassName, allCategories, showAlert, isEditing, setIsEditing, editId, setEditId, noteText, setNoteText, textareaRef }) => {
const [ show, setShow ] = useState(false);
const [ modalText, setModalText ] = useState('');
const charCount = 200;
const handleChange = (event) => {
if (charCount - event.target.value.length >= 0) {
setNoteText(event.target.value);
}
}
const handleSaveClick = () => {
if (noteText.trim().length === 0) {
setModalText('Text cannot be blank!');
setShow(true);
}
if (category === '') {
setModalText('Please select a label');
setShow(true);
}
if (noteText.trim().length > 0 && category!=='') {
showAlert(true, 'Note added', 'success');
handleAddNote(noteText);
setNoteText('');
setShow(false);
}
if (noteText.trim().length > 0 && category!=='' && isEditing) {
setNotes(notes.map(note=>{
if (note.id === editId) {
return ({...note, text:noteText, category:category})
}
return note
}));
setEditId(null);
setIsEditing(false);
showAlert(true, 'Note Changed', 'success');
}
}
const handleCategory = ( event ) => {
let { value } = event.target;
setCategory(value);
}
showHideClassName = show ? "modal display-block" : "modal display-none";
return (
<div className="note new">
<textarea
cols="10"
rows="8"
className='placeholder-dark'
placeholder="Type to add a note.."
onChange={handleChange}
value={noteText}
autoFocus
ref={textareaRef}
>
</textarea>
<div className="note-footer">
<small
className='remaining'
style={{color:(charCount - noteText.length == 0) && '#c60000'}}>
{charCount - noteText.length} remaining</small>
<div className='select'>
<select
name={category}
className="select"
onChange={(e)=>handleCategory(e)}
required
title='Select a label for your note'
defaultValue="Notes"
>
<option value="Notes" disabled selected>Categories</option>
{allCategories.map(item => {
return <option key={item} value={item}>{item}</option>
})}
</select>
</div>
<button className='save' onClick={handleSaveClick} title='Save note'>
<h4>{isEditing ? 'Edit':'Save'}</h4>
</button>
</div>
{/* Modal */}
<main>
<div className={showHideClassName}>
<section className="modal-main">
<p className='modal-text'>{modalText}</p>
<button type="button" className='modal-close-btn'
onClick={()=>setShow(false)}><p>Close</p>
</button>
</section>
</div>
</main>
</div>
)
}
I want the functions passed from App.js to NotesList.js to be in AddNote.js without them being passed in NotesList.js basically minimizing the props destructuring in NotesList.js
Context API does work with function. What you need to do is pass your function to Provider inside value :
<MyContext.Provider value={{notes: notesData, handler: myFunction}} >
For example:
// notes-context.js
import React, { useContext, createContext } from 'react';
const Context = createContext({});
export const NotesProvider = ({children}) => {
const [notes, setNote] = useState([]);
const addNote = setNote(...); // your logic
const removeNote = setNote(...); // your logic
return (
<Context.Provider value={{notes, addNote, removeNote}}>
{children}
</Context.Provider>
)
}
export const useNotes = () => useContext(Context);
Add Provider to your App.js like so:
// App.js
import NoteProvider from './notes-context';
export default App = () => {
return (
<NoteProvider>
<div>... Your App</div>
</NoteProvider>
)
}
Then call UseNote in your NoteList.js to use the function:
// NoteList.js
import {useNotes} from './note-context.js';
export const NoteList = () => {
const {notes, addNotes, removeNotes} = useNotes();
// do your stuff. You can now use functions addNotes and removeNotes without passing them down the props
}

How to chain two filters in react.js

I can't seem to filter two separate things in react.js
I am able to filter by 'hotel ratings using state' but when I tried to add another, it wouldn't work. In the console it says should have a unique key prop.
How can I filter both data sets? currently i'm filtering one data set with
.filter((h) => filter.ratings.includes(h.hotel.starRating))
when I tried creating something similar like this
(filter((room) => extra.occupancy.includes(room.occupancy.maxAdults))
it breaks the code, why is that ?
This is my code:
import { useEffect, useState } from "react";
import './App.css';
export default function App() {
const [hotelRooms, setHotelRooms] = useState([]);
const [filter, setFilter] = useState({ ratings: ["1", "2", "3", "4", "5"] });
const [extra, setExtra] = useState ({occupancy: ["1", "2", "3", "4", "5"] });
const fetchHotels = async () => {
const res = await fetch(
"https://obmng.dbm.guestline.net/api/hotels?collection-id=OBMNG"
);
const hotels = await res.json();
const hotelRooms = [];
for (const hotel of hotels) {
const res = await fetch(
`https://obmng.dbm.guestline.net/api/roomRates/OBMNG/${hotel.id}`
);
const info = await res.json();
hotelRooms.push({ hotel, rooms: info.rooms });
}
setHotelRooms(hotelRooms);
};
useEffect(() => {
fetchHotels();
}, []);
const handleRatingFilter = (e) => {
if (e.target.checked) {
// adding value
const temp = [...filter.ratings];
temp.push(e.target.value);
setFilter({ ...filter, ratings: temp });
} else {
// removing value
setFilter({
...filter,
ratings: [...filter.ratings.filter((v) => v !== e.target.value)]
});
}
};
const handleOccupancyExtra = (e) => {
if (e.target.checked) {
const perm = [...extra.occupancy];
perm.push(e.target.value);
setExtra({...extra, occupancy: perm});
} else {
setExtra ({
...extra,
occupancy: [...extra.occupancy.filter((d) => d !== e.target.value)]
});
}
}
return (
<div className="App">
<div>
{["1", "2", "3", "4", "5"].map((star) => (
<div key={"input-" + star}>
<input
id={"rated" + star}
value={star}
name="ratings"
type="checkbox"
checked={filter.ratings.includes(star)}
onChange={handleRatingFilter}
/>
<label htmlFor={"rated" + star}>Rated {star} star</label>
</div>
))}
</div>
<div>
{["1", "2", "3", "4", "5"].map((adults) => (
<div key={"adults" + adults}>
<input
id={"maximum" + adults}
value={adults}
name="extra"
type="checkbox"
checked={extra.occupancy.includes(adults)}
onChange={handleOccupancyExtra}
/>
<label htmlFor={"maximum" + adults}>adults {adults}</label>
</div>
))}
</div>
{hotelRooms
.filter((h) => filter.ratings.includes(h.hotel.starRating))
.map((h) => (
<div>
<h2> Name: {h.hotel.name}</h2>
<p> Description: {h.hotel.description}</p>
<p> Rating: {h.hotel.starRating}</p>
<p> Postcode: {h.hotel.postcode}</p>
<p> City: {h.hotel.town}</p>
<p style={{ fontWeight: "bold" }}>Rooms:</p>
{
h.rooms.map((room) => (
<div>
<h5>Occupancy</h5>
<div> adults: {room.occupancy.maxAdults}</div>
<div> Children: {room.occupancy.maxChildren}</div>
<div> Maximum guests: {room.occupancy.maxOverall}</div>
<div> Room type: {room.name}</div>
</div>
))}
</div>
))}
</div>
);
}
You can add multiple conditions inside filter.
Example:
const arr = [1,2,3,4,5,6,7,8,9,10]
const evenNums = arr.filter(num => num%2===0)
const biggerThan5 = arr.filter(num => num > 5)
const evenAndBiggerThan5 = arr.filter(num => num > 5 && num%2 === 0)
console.log("evenNums:", evenNums)
console.log("biggerThan5:", biggerThan5)
console.log("evenAndBiggerThan5:", evenAndBiggerThan5)
In your case
{hotelRooms
.filter((h) => filter.ratings.includes(h.hotel.starRating) && /* other condition (e.g. extra.occupancy.includes(...) */)
Codesandbox
This is a codesandbox based on my understanding of what you want to do.

I am trying to do this question but, state is giving me problems

You have a GroceryApp component, which receives a list of products, each one with name and votes. The app should render an unordered list, with a list item for each product. Products can be upvoted or downvoted.
By appropriately using React state and props, implement the upvote/downvote logic. Keep the state in the topmost component, while the Product component should accept props.
const Product = props => {
const {name,votes} = props.product
const plus = () => {
// Call props.onVote to increase the vote count for this product
props.onVote(1,props.index);
};
const minus = () => {
// Call props.onVote to decrease the vote count for this product
props.onVote(-1,props.index);
};
return (
<li>
<span>{name}</span> - <span>votes: {votes}</span>
<button onClick={plus}>+</button>{" "}
<button onClick={minus}>-</button>
</li>
);
};
const GroceryApp = (props) => {
var [products, setProducts] = React.useState(props.products);
const onVote = (dir, index) => {};
return (
<ul>
{products.map((product,index) => (
<Product product={product} index={index} onVote={onVote} />
))}
{/* Render an array of products, which should call onVote when + or - is clicked */}
</ul>
);
}
document.body.innerHTML = "<div id='root'></div>";
ReactDOM.render(
<GroceryApp
products={[
{ name: "Oranges", votes: 0 },
{ name: "Bananas", votes: 0 }
]}
/>, document.getElementById('root')
);
let plusButton = document.querySelector("ul > li > button");
if (plusButton) {
plusButton.click();
}
console.log(document.getElementById('root').outerHTML)
You can update your products and sort them during rendering (can be costly if your list is very large).
const GroceryApp = (props) => {
const [products, setProducts] = React.useState(props.products);
const onVote = (dir, index) => {
const nextProducts = [...products];
const product = products[index];
nextProducts[index] = { ...product, votes: product.votes + dir };
setProducts(nextProducts)
};
return (
<ul>
{products
.sort((a, b) => b.votes - a.votes) // descending sort
.map((product,index) => (
<Product product={product} index={index} onVote={onVote} />
))
}
</ul>
);
}
This worked for me
import React from "react";
const Product = (props) => {
const plus = (ind) => {
props.onVote("+", ind);
// console.log({ ind });
};
const minus = (ind) => {
props.onVote("-", ind);
// console.log({ ind });
};
return (
<li>
<span>{props.product.name}</span> -{" "}
<span>votes: {props.product.votes}</span>
<button onClick={() => plus(props.index)}>+</button>{" "}
<button onClick={() => minus(props.index)}>-</button>
</li>
);
};
const GroceryApp = () => {
let [products, setProducts] = React.useState([
{ name: "Oranges", votes: 0 },
{ name: "Bananas", votes: 0 },
]);
const onVote = (dir, index) => {
const Products = [...products];
if (dir === "+") {
Products[index].votes = Products[index].votes + 1;
setProducts(Products);
} else {
Products[index].votes = Products[index].votes - 1;
setProducts(Products);
}
};
return (
<ul>
{products.map((product, index) => (
<Product key={index} onVote={onVote} index={index} product={product} />
))}
</ul>
);
};
export default GroceryApp;

Filter on a list with headings and keep heading of filtered item(s)

I have a data set where each object has a heading and an items array with multiple values. I need to be able to filter on the items while maintaining their heading. The filter component below works without the headings. The data output I want is something like:
If I filter on 'ap...'
Output:
fruits
apple
const FilterList = () => {
const [searchTerm, setSearchTerm] = useState("")
const [searchResults, setSearchResults] = useState([])
const data = [
{
heading: 'fruits',
items: [
{ item: 'apple' },
{ item: 'orange' },
{ item: 'peach' }
]
},
{
heading: 'veggies'
items: [
{ item: 'carrot' },
{ item: 'broccoli' },
{ item: 'spinach' },
]
}
]
const handleChange = e => {
setSearchTerm(e.target.value)
}
useEffect(() => {
let results = []
if (data && Object.prototype.hasOwnProperty.call(data[0], "heading")) {
data.forEach(item => {
results = item.values.filter(value =>
value.value.toLowerCase().includes(searchTerm)
)
})
setSearchResults(results)
}
}, [searchTerm])
return (
<div>
<input
type="text"
placeholder={'placeholder}
value={searchTerm}
onChange={handleChange}
/>
<Typography>
<strong>{heading}</strong>
</Typography>
<List>
{searchResults.map((value, i) => (
<ListItem key={i}>
{value.value}
</ListItem>
))}
</List>
</div>
)
}
Modify the useEffect :
useEffect(() => {
console.log("changed");
const newSearchResults = data.map(value => ({
heading: value.heading,
items: value.items.filter(item => item.item.includes(searchTerm))
}));
setSearchResults(newSearchResults);
}, [searchTerm]);
Change your return to:
return (
<div>
<input
type="text"
placeholder={"placeholder"}
value={searchTerm}
onChange={handleChange}
/>
<div>
{searchResults.map(value => {
if (value.items.length !== 0) {
return (
<>
<Typography>
<strong>{value.heading}</strong>
</Typography>
<List>
{value.items.map((item, i) => (
<ListItem key={i}>{item.item}</ListItem>
))}
</List>
</>
);
}
})}
</div>
</div>
const data = [{heading:'fruits',items:[{item:'apple'},{item:'orange'},{item:'peach'}]},{heading:'veggies',items:[{item:'carrot'},{item:'broccoli'},{item:'spinach'},{item: 'peach'}]}]
const filterData = (data, val) => {
const result = []
data.forEach(obj => {
let r = obj.items.filter(item => item.item.includes(val))
if(r.length > 0) {
result.push({
...obj,
items: r
})
}
})
return result
}
console.log(filterData(data, "ap"))
console.log(filterData(data, "pe"))
Hope this helps.
Created a sandbox: https://codesandbox.io/s/strange-robinson-tvl7d?file=/src/App.js
Explanation
In essence, two changes are required:
need to restructure your searchResults to have the same structure as data, with heading and items. Instead of just pushing the items found, we specify the corresponding heading also, in the searchResults array
// new filtering function
useEffect(() => {
const results = [];
data.forEach(cate => {
const { heading, items } = cate;
// remains as is
const filterdItems = items.filter(item =>
item.item.toLowerCase().includes(searchTerm)
);
// only if filtered items are found
// add BOTH category & filtered items to results
if (filterdItems.length) {
results.push({
heading,
items: filterdItems
});
}
});
setSearchResults(results);
}, [searchTerm]);
And then, update the render block to loop through the categories array and then the items inside each category
// updated render
{
searchResults.map((resultCate, i) => {
return (
<div>
<strong>{resultCate.heading}</strong>
{resultCate.items.map((item, idx) => (
<div key={idx}>{item.item}</div>
))}
</div>
);
})}

input search filter array using hooks

I want to filter over an array using react hooks. It should be a fairly straight forward task, but I am assuming it is something to do with hooks being updated asynchronously, although this could be wrong.
I am fairly stumped, but have included a code sandbox and my code below:
const teams_data = [
"tottenham",
"arsenal",
"man utd",
"liverpool",
"chelsea",
"west ham"
];
function App() {
const [teams, setTeams] = React.useState(teams_data);
const [search, setSearch] = React.useState("");
return (
<div className="App">
<input
onChange={e => {
const test = teams.filter(team => {
return team.toLowerCase().includes(e.target.value.toLowerCase());
});
console.log("test: ", test);
// uncomment line below and teams is logged as I want
setTeams(test);
setSearch(e.target.value);
}}
type="text"
value={search}
/>
{teams.map(team => (
<p>{team}</p>
))}
</div>
);
}
You need to filter the original data :
const test = teams_data.filter(team => {
return team.toLowerCase().includes(e.target.value.toLowerCase());
});
https://codesandbox.io/s/thirsty-austin-uqx8k
You just need to add another state for search results
const [data , setData] = useState(teams);
const [query, setQuery] = useState('')
const[res , setRes] = useState([]);
return (
<div className="App container">
<form onSubmit = {(e) => e.preventDefault()}>
<input type = "search" className = "srh" placeholder = "search about..."
onChange = {(e) => {
const test = data.filter(team => {
return (
team.toLowerCase().includes(e.target.value.toLowerCase())
)
})
setRes(test)
if(e.target.value === '') setRes([])
}}
/>
</form>
<div>
{
res.map((item , i) => (
<p key = {i}>{item}</p>
))
}
</div>
</div>
);
I've made custom hook.
It receives the array as a first param
the search variable as a second
and the property you want to filter by
I hope it's helpfull
export function useSearch(array: any[], search: string, field: string) {
const filteredArray = array.filter((entry) => {
if (search === "") return entry;
else if (
entry[field].toLocaleLowerCase().includes(search.toLocaleLowerCase())
)
return entry;
});
return {
filteredArray
};
}
Them apply the filtered array to your map function
import { useSearch } from "./useSearch";
import { useState } from "react";
const array = [
{
id: 1,
name: "Humberto Guenzo Yoshimoto"
},
{
id: 2,
name: "Diego Braga"
},
{
id: 3,
name: "Hudson Teixeira"
},
{
id: 4,
name: "Matheus Doimo"
}
];
type FilteredArrayTypes = {
id: number;
name: string;
};
export default function App() {
const [searchByFullName, setSearchByFullName] = useState("");
const { filteredArray } = useSearch(array, searchByFullName, "name");
return (
<div className="App">
<h1>Search list</h1>
<input
onChange={(e) => setSearchByFullName(e.target.value)}
type="text"
value={searchByFullName}
placeholder="search"
/>
{filteredArray.map((entry: FilteredArrayTypes) => {
return (
<ul>
<li>{entry.name}</li>
</ul>
);
})}
</div>
);
}
Here goes a sandbox with the code: here

Categories

Resources