setState inside an async search - react-native

I am trying to set a stateful object with results I get from an async search and I'm getting some errors and strange behavior.
I'm using algoliasearch to run this search. Here's the code in pseudo, I can share the full code if needed, but I don't think it's important. The search works just fine and I get results (see comments below)
const [results, setResults] = useState(null);
index.search(searchQuery, {...requestOptions})
.then(({ hits }) => {
fetchedResultsArray = hits?.map((result) => {
...
return {...}
}
// console.log("fetchedResultsArray", fetchedResultsArray)
setResults(fetchedResultsArray)
).catch
It causing the app to act funny, get stuck, sometimes crash. Sometimes I get this (very long) error: Warning: Please report: Excessive number of pending callbacks: 501. Some pending callbacks that might have leaked by never being called from native code: {"1531":{"module":"NativeAnimatedModule","method":"startAnimatingNode"}...
When I comment setResults(fetchedResultsArray) and uncomment the console log before it, it prints the results and the app acts normal. Reverting back to setResults(fetchedResultsArray) and things go wrong again. I am doing something wrong/illegal, but what?
Another important thing to mention is that I'm using Expo SDK 41 beta 2.
UPDATE
Patrick, you pointed out what I am doing wrong. Let me show you in a little more depth what I do in my SearchScreen, again abbreviated:
const SearchScreen = () => {
// I get the initSearch function from a custom hook and you already see above what the function is.
const [
initSearch,
...
] = useSearch();
// Then I fire the search when the screen loaded
useLayoutEffect(() => {
if (region) {
initSearch({ searchQuery, region, filterOptions, priceOptions });
}
}, [filterOptions, initSearch, priceOptions, region, searchQuery]);
...
my custom hook is something like this:
export function useSearch() {
const [readyToAnimateAfterSearch, setReadyToAnimateAfterSearch] = useState<boolean>(false);
const [results, setResults] = useState<Result[] | null>(null);
const setAlert = useStore(useCallback((state) => state.setAlert, []));
const setProgressBarVisible = useStore(useCallback((state) => state.setProgressBarVisible, []));
const [radius, setRadius] = useState<number>(0);
const initSearch = useCallback(
async ({ searchQuery, region, filterOptions, priceOptions, loadMore = false }) => {
...
},
[results, setResults, setAlert, setProgressBarVisible]
);
return [
initSearch,
results,
setResults,
radius,
readyToAnimateAfterSearch,
setReadyToAnimateAfterSearch,
] as const;
Could you please suggest an alternative way to run this as an answer. No need to go into details, just so I get the flow of how you would handle this. Thank you!

My main issue was me using results inside the initSearch function, instead of using a stateUpdater function and removing results from the deps array.

Related

react query infiniteQuery does not fetch next page

Like the title explains, I can't seem to get the next set of results/page on request using react-query infiniteQuery. I also made a snack from their example code but still nothing.
It looks like the pageParam never gets incremented when fetchNextPage is called. I've tried version 3 and 4 with same results.
So I'm not sure if I should be incrementing the nextCursor manually. I can see nothing in the docs that suggests that I have to do so.
snack example
const fetchProjects = async ({ pageParam = 0 }) => {
const res = await fetch(`https://api.instantwebtools.net/v1/passenger?
page=${pageParam}&size=10`)
return res.json()
}
const List = () => {
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status,
} = useInfiniteQuery({
queryKey: ['test'],
queryFn: fetchProjects,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
});
const loadMore = () => {
fetchNextPage()
}
Turns out the API I was using as test does not return any way for me to tell if there is more data to fetch. ie. no nextCursor property.

Issue rendering data from firestore document in react native

I created a map for the array of exercises in my database, and then for each exercise, which is a document reference, I'm getting the data from that document reference and setting it to a state. This is resulting in an infinite loop right now.
If I remove the setExerciseData line, the console logs the exercise object's data that I'm expecting to see. I'm really not sure what the correct way to render the name field from this data is.
{workout.exercises.map((exercise) => {
async function getData(exercise) {
getDoc(exercise).then((doc) => {
console.log(doc.data());
setExerciseData(doc.data());
});
}
getData(exercise);
return (
<Text>{exerciseData.name}</Text>
)
})}
You need to use useEffect() and setState() to be able to render your data. Also, Firebase Firestore is Asynchronous in nature, as a general note, no one should be trying to convert an Async operation into a sync operation as this will cause problems. You need to use an Asynchronous function to fetch data from Firestore. See sample code below:
const getExerciseData = async () => {
const docRef = doc(db, "<collection-name>", '<document-id>')
const docSnap = await getDoc(docRef)
if (docSnap.exists()) {
// console.log("Document data:", docSnap.data())
setExerciseData(docSnap.data())
} else {
// doc.data() will be undefined in this case
console.log("No such document!")
}
}
useEffect(() => {
getExerciseData()
}, [])
return (
<Text>{exerciseData.name}</Text>
)
You could also check my answer on this thread for more use-cases.

How to use addListener in useEffect

I want to run method when focus screen, i use this:
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
console.log(
'test'
);
});
return unsubscribe;
}, [navigation]);
but it doesnt work. it gives an error like this :
*
An effect function must not return anything besides a function, which
is used for clean-up. You returned: [object Object]
also even i dont return anything, console.log(
'test'
) doest work
I am using navigation V4
Is this working?
import { useFocusEffect } from '#react-navigation/native';
......
useFocusEffect(useCallback(() => {
......
console.log(something)
}, [something]));
//-------
If not check if react navigation is configured correctly.
UPDATE
In React navigation 4.x you will have to follow one of the methods in this guide https://reactnavigation.org/docs/4.x/function-after-focusing-screen/
For useEffect to work properly, the flow is following:
in the square brackets in the end you add a variable which triggers the action. In your case it only triggers on the firs run, and on navigation variable change
you should run your function within useEffect. You have only defined a constant in the body of useEffect, but you never run it.
optionally you may return a function in the end of a run. This function is triggered only when the component unmounts, and used to avoid memory leaks.
Based on this: I'm not sure what are you trying to achieve (unclear from your original post), but this may be what you want:
useEffect(() => {
navigation.addListener('focus', () => {
console.log(
'test'
);
});
const unsubscribe = () => navigation.removeListener('focus'); // !!! I'm not sure about this one, check the docs how to unsubscribe !!!
return unsubscribe;
}, [navigation]); // << triggers useEffect
Assuming you are using the latest version of react-navigation you must the use-focus-effect.
https://reactnavigation.org/docs/use-focus-effect/
Your code should be updated as mentioned below
useFocusEffect(
useCallback(() => {
const unsubscribe = () => {
console.log("test");
}
return () => unsubscribe();
}, [userId])
);

can't get data from server to NuxtJS Store

this is my code :
export const state = () => ({
products: []
});
export const getters = {
getProducts: state => {
return state.products;
}
};
export const mutations = {
SET_IP: (state, payload) => {
state.products = payload;
}
};
export const actions = () => ({
async getIP({ commit }) {
const ip = await this.$axios.$get("http://localhost:8080/products");
commit("SET_IP", ip);
}
});
the server is working nicely but i just can't get the data into the store
First of all, I highly recommend you rename your action and mutation to something like getProducts and SET_PRODUCTS instead of ip. Also make sure you change the variable name inside the action. While this doesn't change any functionality, it makes your code easier to read.
Second, maybe add a console.log(ip) right after you define the const in the action and see if you're getting the data you want in there. In most cases you're going to want to assign ip.data to your variable.
Lastly, make sure you're calling the action somewhere in the code.
You should do it like this:
this.$store.dispatch('getIP'); // Using your current name
this.$store.dispatch('getProducts'); // Using my recommended name

React Native - useEffect / setState - fetching data from api

I'm learning RN and writing a simple app about lottery results.
I am facing this problem:
I need to print the results that i am fetching from an api when the component mounts, using useEffect and setState. Everything works regarding the api call and the item construction, i can console.log the resulting array [loadedResults] which i want to be 'results' in the state.
But, the state its not being modified so the state object remains always empty.
I am clearly missing something in the setState step.
I have tried to make it a function and also using variable.then(...).
I am using a for loop because there are hundreds of results in the api and i only want 10 of them.
this is my code:
const ResultadosScreen = props => {
const [results, setResults] = useState({});
useEffect(() => {
async function fetchData() {
const response = await fetch('apiUrlGoesHere');
// response.json().then((response => setResults(response)));
const resData = await response.json();
const pastResults = resData.past;
const loadedResults = [];
for (let r = 0; r < 11; r++){
loadedResults.push(
new FakeResult(
r,
pastResults[r].drawingDate.substring(0,10),
pastResults[r].nr,
pastResults[r].numbers
)
);
}
console.log(loadedResults); // I GET THE EXPECTED 10 ITEMS IN THIS CONSOLE LOG
(loadedResults => setResults(loadedResults)) //HERE IS WHERE IM MISSING SOMETHING
}
fetchData()
});
console.log(results);
//I GET UNDEFINED FROM THIS CONSOLE LOG
return (
<View style={styles.screen}><Text style={styles.headerTitle}>{}</Text></View>
);
};
export default ResultadosScreen;
Thanks for your help