I have been working on a project that requires fetching a bunch of data and provides multiple ways to filter/search the data, and I though about keeping filtering on the server-side considering I'll be using pagination.
I started using React Context recently and tried an approach that didn't work.
here's the custom React hook I'm using to fetch data
import { useState, useEffect } from 'react';
const useFetch = (url, url_params) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [refresh, setRefresh] = useState(0);
if (url_params) {
url = url + '?' + url_params.toString();
}
const refreshData = () => {
setRefresh((prev) => prev + 1);
}
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [refresh, url]);
return { data, loading, error, refreshData };
};
export default useFetch;
and here is the contextProvider:
import { createContext, useState, useEffect, useContext } from "react";
import { listing_url } from "../../Hooks/api_endpoints";
import useFetch from "../../Hooks/useFetch";
import { useSearchParams } from "react-router-dom";
export const HouseContext = createContext();
export const HouseProvider = ({ children }) => {
const [selectedHouse, setSelectedHouse] = useState(null);
// I have tried multiple approaches for searchParams (one is search params and the other is testParams
const [searchParams, setSearchparams] = useState({
address__icontains: '',
apartment_no__icontains: '',
block__iexact: '',
currency__iexact: '',
floor__iexact: '',
gross__gte: '',
gross__lte: '',
net__gte: '',
net__lte: '',
num_of_floors__iexact: '',
num_of_rooms__iexact: '',
owner__icontains: '',
owner_phone_num__icontains: '',
price__gte: '',
price__lte: '',
status__iexact: '',
view__icontains: '',
});
// second attempt at implementing searchParams
const testParams = new URLSearchParams({
address__icontains: '',
apartment_no__icontains: '',
block__iexact: '',
currency__iexact: '',
floor__iexact: '',
gross__gte: '',
gross__lte: '',
net__gte: '',
net__lte: '',
num_of_floors__iexact: '',
num_of_rooms__iexact: '',
owner__icontains: '',
owner_phone_num__icontains: '',
price__gte: '',
price__lte: '',
status__iexact: '',
view__icontains: '',
});
const { data: houses, error: housesError, loading: housesLoading, refreshData: houseRefresh } = useFetch(listing_url, testParams);
const createHouse = async (house) => {
const newHouse = await fetch(listing_url, {
method: "POST",
body: JSON.stringify(houseData),
});
setHouses([...houses, newHouse]);
}
// useEffect(() => {
// console.log("houses were updated")
// console.log(' search params were updated')
// }, [houses, testParams])
const updateSearchParams = (key, value) => {
console.log("firing updateSearchParams")
houseRefresh();
testParams.set(key, value);
}
return (
<HouseContext.Provider value={{ houses, selectedHouse, housesError, housesLoading, setSelectedHouse, createHouse, testParams, updateSearchParams }}>
{children}
</HouseContext.Provider>
)
}
and here's how I tried to use the filtering options in my Homepage:
import { HouseContext } from '../../Context/House/House';
// .....
export default function Home() {
// .....
const { testParams, updateSearchParams } = useContext(HouseContext)
// ......
return (
// ......
<div className='flex'>
<span className='text-sm/3 my-auto pr-2 dark:text-white'>floor</span>
<input type='number' placeholder='floor' min={0} onChange={(e) => { updateSearchParams('floor__iexact', e.target.value) }} className='w-14 rounded border border-black' />
</div>
// .....
now I have 2 questions about this:
houses
list not getting updated when updateSearchParams
is firing?I believe the issue you are running into here is caused by nothing triggering a re-render, hence you are not seeing any changes with houses.
The easiest way to fix this I believe is the following:
const [testParams, setTestParams] = useState(new URLSearchParams({ ... rest of the tings })
Then update updateSearchParams to do the following instead of the testParams setting you were doing:
setTestParams(prev => {
const newParam = new URLSearchParams(prev);
newParam.set(key, value);
return newParam;
});
This changing should now cause React to trigger a re-render and hopefully show you the updated houses.
I would also be careful with the onChange you are currently doing, this can cause some issues and a lot of requests being sent.