Last active
November 20, 2022 18:11
-
-
Save channyeintun/1605d1ecae5b8498b8952d7513379dc6 to your computer and use it in GitHub Desktop.
Optimized React Custom Filter Hook
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useState } from "react"; | |
import { useSelector } from "react-redux"; | |
/* | |
Instead of JSON.stringifying the array of objects to use as deps in useMemo | |
hook, I've created custom hook that generate serial string which can be used | |
to decide whether filtered result should be updated or not. So, filtered result | |
can be used in useMemo hook without JSON.stringifying. | |
e.g | |
useMemo(()=>something,[JSON.stringify(collection)]); | |
Above code is without custom hook where useMemo cannot check | |
elements are same or not in the collection which is object type. | |
useMemo hook check only the references to deps. | |
So, we can change collection as string value with the help of | |
JSON.stringify(). | |
JSON.stringifying an array of objects which is enomous can cost performance. | |
Therefore, generating serial string in filtering loop can optimize the performance. | |
Later, we can use that serial string to decide whether we are going to update the result or not. | |
*/ | |
export function useCustomFilter(options) { | |
const [filteredData, setResult] = useState([]); | |
const [serialString, setSerialString] = useState(); | |
const [filterKeyword, setFilterKeyword] = useState(''); | |
const collection = useSelector(state => state.your.collection); // initial collection | |
useEffect(() => { | |
if (!(collection instanceof Array)) { | |
throw new Error('Collection must be an array.'); | |
} | |
const size = collection?.length ?? 0; | |
if (options.uniqueKey | |
&& typeof options.callback == 'function') { | |
let tempArray = []; | |
let serialTempString = ''; | |
for (let i = 0; i < size; i++) { | |
let obj = collection[i]; | |
let isTruthy = options.callback(obj, filterKeyword); | |
if (isTruthy) { | |
serialTempString += obj[options.uniqueKey] | |
tempArray.push(obj); | |
} | |
} | |
if (serialString !== serialTempString) { | |
setResult(tempArray); | |
setSerialString(serialTempString); | |
} | |
} else { | |
throw new Error('Options for uniqueKey and callback are required.'); | |
} | |
}, [filterKeyword, collection]); | |
// you can remove serialString if you don't like to include it in the returning values | |
return [filteredData, setFilterKeyword, serialString]; | |
// now, you can use filteredData as a dep or in useMemo hook | |
// since its reference won't change until the elements inside are changed | |
// Or you can simply use serialString as a dep instead of filteredData | |
// you can achieve the same goal | |
} |
/* uniqueKey can be the key of the object you search for
e.g username
The following object is an example of an item in the collection.
{
username:'johndoe',
age:12
}
You can implement the callback like the following
function callback(obj,keyword){
return obj.username===keyword
}
*/
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can use setFilterKeyword with debounce function. In that way, you can reduce the amount of triggering the same action.