I am just trying to figure out a weird issue in my React project. So I am executing some code in my useEffect. As you can see, I am trying to grab the currentObj from an array of objects based on searchTerm. However, there seems to be a race condition in getting the value searchTerm. The toggleFilter and setAvailabilities functions below are throwing an error saying that the id is not defined. And the page crashes.
useEffect(() => {
const search = window.location.search;
const params = new URLSearchParams(search);
let param = "";
if (search.includes("neighborhood")) {
param = params.get("neighborhood");
const searchTerm = search.toLowerCase();
if (searchTerm) {
const currentObj = searchTerm && locations.find(item => item.linktitle.toLowerCase().includes(searchTerm));
console.log(searchTerm, currentObj);
toggleFilter(currentObj.id);
setAvailabilitiesFilter([currentObj.id]);
}
}
return () => resetAllFilters();
}, []);
However, if I hardcode a value into it, it actually renders ok and the page does not crash.
const currentObj = searchTerm && locations.find(item => item.linktitle.toLowerCase().includes('manhattan'));
That is why I am thinking it is a race condition in getting the id. Also when I look at the console log from hardcoded searchTerm, it is showing the searchTerm property. But I do not want to hardcode it.
But of course, I don't want to hardcode the value as I expect to dynamically render the searchTerm
Is there any way I can in the useEffect wait until searchTerm is defined before running that .find method? I also tried adding the searchTerm in the dependency array but that doesn't seem right as it is not giving suggestions there and seems not to be scoped there.
Any help appreciated. Thanks!
CodePudding user response:
Have you tried this?
const search = window.location.search;
search.substring(1) // this will ignore "?" in the result which could be causing the issue on finding the correct object
Have you tried printing the value of search here?
console.log(search);
if yes, what is it that you see?
Also, you don't need to repeat yourself by doing this:
if (searchTerm) {
const currentObj = searchTerm && locations.find(item => item.linktitle.toLowerCase().includes(searchTerm));
...
}
You are already checking if searchTerm has been defined inside your if statement so you can simply do this:
if (searchTerm) {
const currentObj = locations.find(item => item.linktitle.toLowerCase().includes(searchTerm));
...
Finally, to avoid any other errors before using your functions you can check if you actually have an id or not like this:
if(currentObj.id) {
toggleFilter(currentObj.id);
setAvailabilitiesFilter([currentObj.id]);
}
Hope that helps!
CodePudding user response:
It seems the issue is that you are using the entire query search string in the .find
predicate callback.
useEffect(() => {
const search = window.location.search; // <-- entire search string
const params = new URLSearchParams(search);
let param = "";
if (search.includes("neighborhood")) {
param = params.get("neighborhood");
const searchTerm = search.toLowerCase(); // <-- still entire search string
if (searchTerm) {
const currentObj = searchTerm && locations.find(
item => item.linktitle.toLowerCase()
.includes(searchTerm) // <-- still entire search string
);
console.log(searchTerm, currentObj);
toggleFilter(currentObj.id); // <-- throws error if undefined currentObj
setAvailabilitiesFilter([currentObj.id]);
}
}
return () => resetAllFilters();
}, []);
Array.prototype.find
returns undefined when no array element is found using the predicate function, and the code does not properly check for a truthy result value prior to attempting to access into it for nested properties like id
.
I'm going to guess at this point that you are wanting to search the link titles for a matching neighborhood value, which is param
in your code.
useEffect(() => {
const search = window.location.search;
const params = new URLSearchParams(search);
// get the neighborhood querystring param
const neighborhood = params.get("neighborhood");
// check if it exists
if (neighborhood) {
// it does, search the locations array
const currentObj = locations.find(
({ linktitle }) => linktitle.toLowerCase().includes(neighborhood.toLowerCase())
);
// check if result was found
if (currentObj) {
// it was found, access into object to get id property
toggleFilter(currentObj.id);
setAvailabilitiesFilter([currentObj.id]);
}
}
return () => resetAllFilters();
}, []);