I have a component that looks like this
import { useState, useEffect, useMemo } from 'react'
import Input from '~/components/Input'
import Fuse from 'fuse.js'
interface Props {
data: any[]
keys: string[]
onUpdate: (data: any[]) => void
placeholder: string
}
const FilterInput = ({ data, keys, onUpdate, placeholder }: Props) => {
const [search, setSearch] = useState('')
const handleSearch = (e) => setSearch(e.target.value)
// Memorize the fuse instance to avoid recreating it on every render
const fuse = useMemo(() => {
return new Fuse(data, {
includeScore: true,
keys,
threshold: 0.3,
distance: 100,
})
}, [data, keys]) //THIS IS THE RELEVANT PART
useEffect(() => {
const results =
search === '' ? data : fuse.search(search).map((result) => result.item)
onUpdate(results) // Invoke onUpdate with the filtered or original list
}, [search, fuse, onUpdate, data]) // Effect dependencies
return (
<div className={'mb-10'}>
<Input value={search} onChange={handleSearch} placeholder={placeholder} />
</div>
)
}
export default FilterInput
I am using it like this
const ProjectFeatureListing = ({ projects }: Props) => {
const [filteredProjects, setFilteredProjects] = useState(projects) // State to hold the filtered list
console.log(projects)
return (
<div className={'max-w-7xl py-10 mx-auto px-10'}>
<FilterInput
data={projects}
keys={['title', 'description']}
onUpdate={setFilteredProjects}
placeholder={'Find our creators'}
/>
...
I can’t figure out why sending this static array would trigger a re-render since it’s not changing, to be clear when I do this
const fuse = useMemo(() => {
return new Fuse(data, {
includeScore: true,
keys,
threshold: 0.3,
distance: 100,
})
}, [data]) //THIS IS THE RELEVANT PART
It does NOT infinitely re-render
===
UPDATE:
I added this code
// Ref to store the previous keys
const prevKeysRef = useRef(keys)
// Check if keys have the same reference
const keysHaveSameReference = keys === prevKeysRef.current
console.log('Keys have the same reference:', keysHaveSameReference)
// Update prevKeysRef with the current keys
useEffect(() => {
prevKeysRef.current = keys
}, [keys])
And I can confirm that the ref to keys is changing
2
Answers
I've found a possible solution
use a reference so that fuse is only created once.
Probably, when
onUpdate
in youruseEffect
changes the state ofProjectFeatureListing
, the rerender recreates the['title', 'description']
object and theFilterInput
gets a new reference to another object with the same properties, which is why the callback in useMemo is called and thefuse
variable changes, when changed,useEffect
is called withonUpdate
and everything starts all over again.