skip to Main Content

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


  1. Chosen as BEST ANSWER

    I've found a possible solution

    let fuse = useRef(
        new Fuse(data, {
          includeScore: true,
          keys: keys,
          threshold: 0.3, // Adjust the strictness of the search
          distance: 100, // Adjust how far the algorithm looks for a match
        }),
      )
    

    use a reference so that fuse is only created once.


  2. Probably, when onUpdate in your useEffect changes the state of ProjectFeatureListing, the rerender recreates the ['title', 'description'] object and the FilterInput gets a new reference to another object with the same properties, which is why the callback in useMemo is called and the fuse variable changes, when changed, useEffect is called with onUpdate and everything starts all over again.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search