I created a rough implementation of a component that lists stuff based on data has the option to enable searching for that data. I've not used React all that much so it's probably a terrible way to implement it. I looked into passing state upwards and the most common way I saw was to do what I did and pass the setter function as a prop. One thing that isn't clean is having to pass renderBans
as a prop but I wasn't sure how else to render each item from the given data and update the state.
The list component
import Ban from './Ban'
import styles from '../styles/BansList.module.css'
import React, { useState, useEffect } from 'react';
import Search from './Search'
const BansList = ({title, bans, search=false}) => {
const bansPerRender = 3;
const [bansToRender, setBansToRender] = useState();
const [next, setNext] = useState(3);
const showMore = () => {
setBansToRender(renderBans(bans.slice(0, next+bansPerRender)))
setNext(next+bansPerRender)
}
const renderBans = bans => {
let holdBans = []
bans.map((ban, index) => {
// convert from Unix timestamp to HH:MM:SS
let banDate = new Date(ban.timestamp*1000)
// if ban not expired
if(new Date().getTime() < banDate.setMinutes(banDate.getMinutes() + ban.bantime))
isExpired = false
// reset banDate to original
banDate = new Date(ban.timestamp * 1000)
let permaBan;
ban.bantime === 0 ? permaBan = true : permaBan = false;
holdBans.push(<Ban key={index} offenderName={ban.offender_name} offenderSteamId={ban.offender_steamid} banDate={banDate} adminName={ban.admin_name} banReason={ban.reason} banLength={ban.bantime} isExpired={false} permaBan={permaBan}/>)
})
return holdBans;
}
useEffect(() => {
setBansToRender(renderBans(bans.slice(0, bansPerRender)))
}, [])
return (
<>
<div className={styles.wrapper}>
{search && <Search toSearch={bans} sendResults={setBansToRender} renderFunc={renderBans} resultsToSend={bansPerRender}/>}
<h1>{title}</h1>
{bansToRender}
<button onClick={() => {showMore()}}>Show More</button>
</div>
</>
)
}
export default BansList;
The search component
import React, { useState, useEffect, useContext } from 'react';
import Fuse from 'fuse.js'
import SearchResults from './SearchResults'
const Search = ({toSearch, sendResults, renderFunc, resultsToSend='all'}) => {
// setup state for search
const [query, setQuery] = useState("");
const [searchResults, setSearchResults] = useState();
let isExpired = true;
const search = (query) => {
if(!query) {
setSearchResults(toSearch)
return;
}
const fuseGuardban = new Fuse(toSearch, {
keys: ["offender_name", "admin_name", "offender_steamid"],
useExtendedSearch: true
});
const result = fuseGuardban.search(query)
const matches = []
if (!result.length) {
setSearchResults([]);
} else {
result.forEach(({item}) => {
matches.push(item);
});
setSearchResults(matches);
}
if(searchResults || searchResults == [])
sendResults(renderFunc(searchResults.slice(0, resultsToSend)))
}
return (
<>
<input type="search" placeholder="Search by name" onChange={e => search(e.target.value)}/>
<span>{query}</span>
</>
)
}
export default Search;