React Native - useEffect / setState - fetching data from api - react-native

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

Related

fetch returns first empty array, after that it returns response

I'm making my final degree project and now I'm involved in the part to show towns on a map using data from a backend URL.
The problem is that fetch returns at first an empty array, and I need it to stay loading until the variable is a valid JSON.
const [isLoading, setLoading] = useState(true);
const [markers, setMarkers] = useState([]);
const getTowns = async () => {
try {
const response = await fetch(`${baseUrl}/towns`);
const json = await response.json();
setMarkers(json.data);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
getTowns();
}, []);
Another question is: Why when I put a console.log('whatever') it appears more than one time on the console. Don't understand why
What I need is fetch to setLoading(false) only when response is a JSON, not an empty array
What you can do is add these 2 hooks to your code:
A state for the returned response (with initial value an empty array)
A useEffect which will set to false isLoading once the response state change value
const [response, setResponse] = useState([]);
const getTowns = async () => {
try {
setResponse(() => await fetch(`${baseUrl}/towns`));
const json = await response.json();
setMarkers(json.data);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
setLoading(() => false);
}, [response]);
your code is fine you juste don't need the isLoading useState Hook. you can test with the value of markers
Why when I put a console.log('whatever') it appears more than one time on the console. Don't understand why
when the component first render getTowns runs in useEffect and since it updates the state the component renders again. learn more here about react component lifecycle
what I suggest is when you are returning your jsx you check if markers is still empty
const [markers, setMarkers] = useState('');
if(markers === ''){
return (<div>Loading...<div/>)
} else {
return(<>
.... what you need to return in you component
</>)
}

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.

React Native Hooks initializer not taking the correct value

What I am trying to do is sync a list of attendees from an online database, and if the current user is in the list, then disable a button, else enable the button.
I am using react native hook (I am not sure if I am using the term correctly as I am fairly new to react), in order to set the value of disabling the button.
The issue that I am facing is that the value is getting initialized to false, even tho it should clearly get initialized to true.
After adding some logging I made sure that the function is executing correctly and reaching the code where it sets the value to true.
const [buttonDisabled, changeButtonState] = useState( () => {
var database = firebase.database();
var userId = firebase.auth().currentUser.uid;
const dbRef = firebase.database().ref();
var Attendees = [];
var disable = false;
dbRef.child("gameAttendees").child(gameinfo.gameID).get().then((snapshot) => {
if (snapshot.exists()) {
Attendees = snapshot.val().Attendees;
for(var i=0;i<Attendees.length;i++){
if(Attendees[i]==userId){
return true;
}
}
} else {
console.log("no value");
return false;
}
}).catch((error) => {
console.error(error);
});
});
Adding an example of an async mount effect:
const Comp = () => {
const [s, setS] = useState(); // State will be undefined for first n renders
useEffect(() => {
// Call the async function and set the component state some time in the future
someAsyncFunction().then(result => setS(result));
}, []); // An effect with no dependencies will run only once on mount
return </>;
};

Run An Function Only Once Per App Installation In React Native ( Class Component )

So I Have A Issue Here I Just Want To Run An Function Only Once In The App
I Need To Initialize An Empty Array Only Once To The Async Storage . The I Will Do Read And Write Functions In The Array Later On Using ComponentWillMount() ...
The Issue Is How Do I Initialise A Empty Array at First And I Don't Want It To Be Empty Later On ( Like When The App Is Closed And Opened )
Each Time The Screen Renders I Read The Data Stored In The Async Storage ... But When The Screen Renders For The First Time The App Is Installed ... I Have Not Called The Below Function
setObjectValue = async () => {
try {
const jsonValue = JSON.stringify(this.state.data)
await AsyncStorage.setItem('Emails', jsonValue)
this.setState({ value : this.state.data.length })
} catch(e) {
// save error
}
console.log('Done.')
}
But In Case I Call It Before The Reading ... It Erases The Existing Data And Sets It To An Empty Array ( Becuase I Have An Contructor Which Has data: [ ] ) so when i read the data i get an empty array
You Can See The Full Code Here - https://snack.expo.io/#belgin/budget-tracker
Thanks To Any One Who Helps : )
There are few changes that are required in your app.
First replace UNSAFE_componentWillMount with componentDidMount
And change your getMyObject like below which will make sure that it only updates if the value is there
getMyObject = async () => {
try {
const jsonValue = await AsyncStorage.getItem('Emails');
if (jsonValue) {
const data = JSON.parse(jsonValue);
this.setState({ data: data, value: data.length });
}
} catch (e) {
// read error
}
};
Also remove the setState calls from the functions that you update the asyncStorage this will show invalid data as your calls are done while the state is not updated.
Also you can update the asyncStorage after the state is updated like below
this.setState({ data: this.state.data },()=>this.setObjectValue());

how to change params value passed to axios route to change data react native?

I am building a kind of book app (Holy Quran )... user will go for a list of Surahs, each Surah contains around 5 - 50 pages. I managed navigating user from the list to first page of each Surahs.. and through getting an api request data for first page will be shown and this is the code in the showScreen
const [quran, setQuran] = useState([]);
const page = navigation.getParam('page');
const name = navigation.getParam('name');
let pageNumber = page;
useEffect(() => {
Quran();
}, []);
const Quran = async () => {
const response = await QuranApi.get(`/${pageNumber}/quran-uthmani`);
setQuran(response.data.data.ayahs);
}
so let's imagine that first page is page number 200, I am looking for some way so when user clicks go to page 201 or 199 (next or previous) and refetching the data so show for him requested page
I need some help here please and thanks in advance
Basically you need to add some sort of button or any element in your 'markup section' which will trigger next/previous action. For example:
// The following line makes react listen for changes in page state variable.
// If you use setPage() anywhere, react will auto update all the components
// where page variable is used. You don't need to manually do it
const [page, setPage] = useState(nav.getParam('page'))
// ....
// This function does exactly that
const handleClick = event => {
if(event.target.value === 'next-page') // If next button was pressed
setPage(page + 1) // We increment the page state variable by 1
else if(event.target.value === 'prev-page') // If prev button was pressed
setPage(page - 1) // We decrement the page state variable by 1
// By doing so, react will auto update every element which uses this variable
}
// .....
// We tell react, if [page] state variable changes by user clicking
// next or previous button, fetch data from the api using updated
// page number and using that, we update the [quran] variable using
// 'setQuran()' function
useEffect(async () => {
const response = await QuranApi.get(`/${page}/quran-uthmani`)
setQuran(response.data.data.ayahs)
}, [page] );
//......
// markup section
return(
//....
<Button onClick={handleClick} value="next-page">Next Page {page + 1}</Button>
<Button onClick={handleClick} value="prev-page">Prev Page {page - 1}</Button>
//....
)
Thank you dear #Sayed it finally works but I have to make some modifications with the same idea to be
const [page, setPage] = useState(navigation.getParam('page'))
const handleRightClick = () => {
setPage(parseInt(page) + 1)
};
const handleLeftClick = () => {
setPage(parseInt(page) - 1);
}
useEffect(() => {
return () => {
console.log("cleaned up");
};
}, []);
useEffect(() => {
Quran();
}, [page]);
<Button title = 'Next Page' onPress = {handleRightClick}/>
<Button title = 'Prev-page' onPress ={handleLeftClick} />
So it's working well in that case without an error
Try this small change...
const [page,setPage]=useState(nav.getParam('page'))
useEffect(() => {
Quran();
}, [page]);
by passing an empty array as a second parameter to the useEffect function, you are telling react to only execute the side effect once (at mount time).
However if that array contains a list of state variables to watch for. React will only re-run the side effect if one of the items in this array changes.
The above mentioned change in useEffect would make your useEffect sensitive to any change in the state variable (page) passed to the useEffect array. As you pass new pagenumbers in the url as a parameter, it would result in calling useEffect everytime as the page state variable is set.
Hope this helps!