Modifying state in react-query's query function - reactjs

I have written a wrapper around fetch that lets me handle error states from my API like a 401. When it gets an error, the custom fetch function sets some state using react hooks.
Because I use hooks in the function, I cannot just import it normally. Therefore, I pass this wrapped function around using context.
When I use react-query, I would like to just use this wrapped function in the following way:
function myQuery(key, apiFetch) {
return apiFetch(...)
}
function MyComponent() {
useQuery('key', myQuery)
}
In this example, apiFetch is an available argument in the query function.
One option I do have here is to just inline the function like so:
function myQuery(key, apiFetch) {
return apiFetch(...)
}
function MyComponent() {
const apiFetch = useApiFetch();
useQuery('key', (key) => apiFetch(...))
}
However I find this a little messy and would like to keep the query/mutation functions separate if possible.
Does anyone know of an approach I can take to have my apiFetch function be available to me in react-query's functions?

If you don't want to repeat calling useApiFetch and useQuery hooks in multiple components, you can extract both hooks into another custom hook that integrates react-query with your fetch hook:
function useApiQuery(param) {
const apiFetch = useApiFetch();
return useQuery(['key', param], (key) => apiFetch(...))
}
Then use it in your component:
function MyComponent({ param }) {
const { data, error } = useApiQuery(param);
return (...);
}

Related

RTK-query: how to have the fixedCacheKey approach also for useLazyQuery?

reading the documentation I saw that mutations can share their result using the fixedCacheKey option:
RTK Query provides an option to share results across mutation hook instances using the fixedCacheKey option. Any useMutation hooks with the same fixedCacheKey string will share results between each other when any of the trigger functions are called. This should be a unique string shared between each mutation hook instance you wish to share results.
I have a GET api call that I need to invoke using the trigger method of the useLazyQuery hook, but I need to exploit its booleans (isSuccess, isError, etc...) in many places. Is it possible to have the same behaviour for the useLazyQuery hooks ?
In RTK-query, queries instances follow this logic in cache (from documentation):
When you perform a query, RTK Query automatically serializes the request parameters and creates an internal queryCacheKey for the request. Any future request that produces the same queryCacheKey will be de-duped against the original, and will share updates if a refetch is trigged on the query from any subscribed component.
Given that the only way, in different components, to get the same cache entry is to pass the same query parameters. If you have many useLazy in many components, those hooks don't point any cache entry until you fire them (with some parameters).
This is what I can understand from the official docs
I ended up implementing a custom hook to handle this scenario
// This is the custom hook that wrap the useLazy logic
const useCustomHook = () => {
const [trigger] = useLazyGetItemQuery();
const result = apiSlice.endpoints.getItem.useQueryState(/* argument if any */);
const handleOnClick = async () => {
// The second argument for the trigger is the preferCacheValue boolean
const { data, error } = await trigger(/* argument if any */, true);
// Here where I will implement the success/error logic ...
}
return { onClick: handleOnClick, result}
}
Setting the preferCacheValue at true forced the RTQK to return the cached value if present for the lazy I'm using. That allow me both to avoid triggering many API calls.
Once implemented the hook above I can do the following:
// Component that will make the API call
function Header() {
const { onClick } = useCustomHook();
return (<Button onClick={onClick}>Click me</Button>);
}
// Component that will use the booleans
function Body() {
const { result: { isFetching } } = useCustomHook()
return (<Spin spinning={isFetching}>
<MyJSX />
</Spin>);
}
With this approach I was able both to centralize the trigger logic (e.g. which parameters I have to pass to my trigger function) and exploiting the booleans of that RTKQ hooks in many components.

Is there really no way to define a "component" without UI in React?

I'm struggeling already some days now. I'm searching for a way to define a React component without UI. My goal is to have several functions in one "component" (as I said without UI), which can be used by other components, but with all the benefits of useState etc.
I already, thought, there is a way, by defining components like this:
export const useExample = () => {
const [someVariable, setSomeVariable] = useState('someValue');
const getSomething = (parameter1, parameter2) => {
return ...
}
return {
getSomething
};
}
I thought, I could import useExample in a component and then use the function getSomething like this:
import { useExample } from 'useExample.component.js';
//...
function SomeComponent(props) {
//...
useEffect(() => {
const example = useExample.getSomething(parameter1, parameter2);
},[]);
//...
}
...but actually this seems not be working.
Then I get an error like this:
Uncaught TypeError:
...useExample.getSomething is not a function
at eval...
All docs I find is telling me, that I need to return a JSX element. But I really cannot believe that in React there is no way to do this. Am I wrong?
I know, if I use plain JS, I'm able to do so, but I already tried this approach and there are many other difficulties then with combining it with React components.
Did anyone have had a similar issue and found a solution to this?
What you're trying to create is a custom hook, not a component.
You cannot call a hook in a useEffect, but since it returns an object containing the function, you can store this function in a variable and then call it in the useEffect.
function SomeComponent(props) {
const { getSomething } = useExample();
useEffect(() => {
const example = getSomething(parameter1, parameter2);
},[]);
//...
}
Also be aware that if you call this function in the useEffect, you probably want to put it in the dependency array. And depending on the rest of your code, it also means you might need to wrap getSomething in a useCallback.

await useFetch and then passing data to another react hook

I have a simple useFetch() react hook that makes an api call to fetch data, I then want to pass that data to a custom usePagination() hook that processes the data and returns some of it to display.
something like this
const [data] = useFetch(`http://localhost:whatever`);
const {postDisplay} = usePagination(data);
The problem is that data is undefined until it finishes fetching, and usePagination crashes.
and being that it is a hook you can't call conditionally.
(I guess I can make useFetch async and await it, but it doesn't feel like that's the solution here)
Is there any easy ways around this?
You can handle the condition inside the usePagination hook itself. If it receives undefined as the argument, it should still call all the other hooks it could have (like useMemo or useState), but bail out of all of them. Eg.:
function usePagination(data) {
const postDisplay = useMemo(() => {
if (!data) return null
// business as usual here
}, [data])
return {postDisplay}
}
If your hook doesn't call any other hooks, you can just return early:
function usePagination(data) {
if (!data) return {postDisplay: null}
// business as usual here
return {postDisplay}
}
And inside your component, you handle the case when postDisplay is null as well.

How do to pass parameter to a custom hook

How do I pass a parameter from my return statement to the custom hook, I have created everything but just the method to pass the parameter to the custom hook is my problem.
const {
geodata: address,
isPending,
geoerror,}
= useGeo(latitude, longitude);
You see useGeo that's the custom hook where I want to pass latitude and longitude (This works but I don't know to pass the parameter from the return statement)
So I tried method like this:
const getAddApi = (latitude, longitude) => useGeo(latitude, longitude);
and in the return statement I tried this below but it didnt work I keep getting Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
<Text>{getAddApi(item.latitude, item.longitude).geodata}</Text>
Custom hook is follows:
a function
call at least one build-in hook inside
used inside a function component (this is your error)
Suppose I design a custom hook like this,
const useGeo = (latitude) => {
const [state] = useState(latitude)
return state
}
It's a simple function with input latitude and return state. Now you can use it in a function component.
const Title = () => {
const s = useGeo(273)
...
}
This is what a custom hook about. It's used mainly for algorithm refactoring. If you want to know more about input and output, just ask what you normally do for a function. The rest is the definition of the hooks that you have to meet which i listed above.

Changing the function to a reusable hook in React.js in functional component

How can I transform the below function checkStatus to a reusable hook in React? I am returning the boolean value in the render component.
const someComponentChecker = (props) => {
const { isEnabled, isTrueMode, datasItems } = props
function checkStatus = (data, isEnabled) => {
if(isTrueMode && !isEnabled){
return !data.isUpdate
}else if(!isEnabled){
return !data.isCreate
}else{
return true
}
}
return (
datasItems && datasItems.map((data, index) => {
return <SampleComponent
enabled={checkStatus(data, isEnabled)}
/>
})
)
}
A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.
Unlike a React component, a custom Hook doesn’t need to have a specific signature. We can decide what it takes as arguments, and what, if anything, it should return. In other words, it’s just like a normal function. Its name should always start with use so that you can tell at a glance that the rules of Hooks apply to it.
If you feel that your function fulfills these criteria you can simply extract the code and create a new file with prefix 'use'.
But for your 'checkStatus' it doesn't seem like you are using any hook inside that. And its a very simple function. may be you can just make a util function and use it wherever you want.

Resources