0

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 2

0

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Ok, I think i follow but I'm still not sure how to solve it.
You can move this array ['title', 'description'] outside the ProjectFeatureListing component as a constant, or, if you plan to change it in the future, put it in a separate state.
The thing is, this puts the responsibility on the parent to not send an inlined array, I want the child component to take care of it
Then you can try something like what was answered here: stackoverflow.com/questions/65125885/…
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.