ok this is a very simple action that i want to do i just want to fill a RNPickerSelect with the data that i recibe from a http request , the thing is i am learning how to use react-native and i am little confused. This is the code i have :
import React,{useState} from 'react';
import RNPickerSelect from 'react-native-picker-select';
import { View, Text, Button } from 'react-native';
const getData = () => {
const xhr = new XMLHttpRequest();
xhr.open('GET','http://3.86.214.41/api/v1/web/activities');
xhr.responseType = 'json'
xhr.onload = () => {
const data = xhr.response
for (var i = 0; i < data.length; i++) {
alert(data[i].id + data[i].name);
}
}
xhr.send();
}
const Actividad = () => {
const [activityId,setActivityId] = useState('');
return (
<View>
<Text>Selecciona Actividad</Text>
<RNPickerSelect
onValueChange={activityId => setActivityId(activityId)}
items={
[ {label: 'actividad', value: '1'}]
}
/>
<Button onPress={getData} title="Get Activities" />
</View>
);
};
export default Actividad;
you can see the response i iterate just for make sure the data is there , so i want to make the list of items with that data.
from the getData funtion to the items prop inside the RNPickerSelect component hope i am clear enough so please if someone can help me i will vote your answer for thank you so much. sorry if this is to dump question but i can't find a clear and easy undertandable answer yet thanks.
You will need to add an extra state to your component,
const [items,setItems] = useState([ {label: 'actividad', value: '1'}]);
Then pass this state to your RNPickerSelect:
<RNPickerSelect
onValueChange={activityId => setActivityId(activityId)}
items={items}
/>
the rest is just change this items state.
we can create another function lets call it onEndGetData end this will receive your endpoint data and update items state
const onEndGetData = (payload) => {
setItems(payload.map((item) => ({ label: item.name, value: item.id })));
};
And now just pass this function to getData function so you can set state according
<Button onPress={() => getData(onEndGetData)} title="Get Activities" />
so in your getData you can use it once you have the response:
const getData = (onEndGetData) => {
...
xhr.onload = () => {
onEndGetData(xhr.response);
}
Related
I have a Firestore collection, schemed as follows:
posts{
uid{
userPosts{
postID{
creation:
postText:
}
}
}
}
I want to display all of the posts, so I've made the corresponding queries and saved them in posts - an array of all the posts that I later iterate through.
The problem with the way I do it is that it keeps adding the same posts every render. So I've tried to set the array each time, but that way the code never passes through these posts && posts.length > 0 condition.
I'm really new to RN and JS in general, but what I was expecting is
Nothing to show here
at first, and then the list of posts.
The complete component:
import { Text, Pressable, FlatList, SafeAreaView } from "react-native";
import { globalStyles } from "../../styles/global";
import React, { useState, useEffect } from "react";
import { db } from "../../../firebase";
import Post from "../../API/Post";
import { collection, getDocs } from "firebase/firestore";
const FeedScreen = ({ navigation }) => {
const [posts, setPosts] = useState([]);
useEffect(() => {
const getPostData = async () => {
setPosts([]); // ---> Without this line the posts keeps adding each render
const q = collection(db, "posts");
const docSnap = await getDocs(q);
docSnap.docs.map(async (item) => {
const tmp = collection(db, "posts", item.id, "userPosts");
const tmpSnap = await getDocs(tmp);
tmpSnap.docs.map(async (element) => {
setPosts((prev) => {
prev.push(element.data());
return prev;
});
});
});
};
getPostData().catch(console.error);
return;
}, []);
return (
<SafeAreaView style={globalStyles.global}>
{posts && posts.length > 0 ? (
<FlatList
data={posts}
renderItem={({ item }) => (
<Post
post={item}
navigation={navigation}
style={globalStyles.list_of_posts}
/>
)}
keyExtractor={(item, index) => index.toString()}
/>
) : (
<Text>Nothing to show here</Text>
)}
<Pressable
title="edit"
onPress={() => {
navigation.navigate("CreatePost", { navigation });
}}
style={globalStyles.plus_btn}
>
<Text style={globalStyles.plus_btn_text}>+</Text>
</Pressable>
</SafeAreaView>
);
};
export default FeedScreen;
As said, I'm new to this so I'd love an explanation of what actually happens and how to do it properly.
I think the prev value of setPosts will always be [] since it does not immediately update if you call it. A standard way to do it is to call setPosts at the end of your function. Can you try this one?
useEffect(() => {
const getPostData = async () => {
const q = collection(db, "posts");
const docSnap = await getDocs(q);
const promises = docSnap.docs.map(async (item) => {
const tmp = collection(db, "posts", item.id, "userPosts");
const tmpSnap = await getDocs(tmp);
return tmpSnap.docs.map((element) => element.data());
});
const arrayOfPosts = await Promise.all(promises);
let newPosts = [];
arrayOfPosts.forEach((posts) => {
newPosts = [...newPosts, ...posts];
});
setPosts(newPosts);
};
getPostData().catch(console.error);
return;
}, []);
export function Login() {
const [skip, setSkip] = useState(true);
const { data, isFetching } = useVerifyUserQuery(userState, {
skip,
});
const LoginButton = () => (
<Button
title="Login"
onPress={() => {
setSkip((prev) => !prev);
}}
/>
);
return (
…
)
}
The requirement is to make a request when the button is pressed, and then store the returned data in a constant. Is there a good way to make sure data is returned before I store it.
Here is one of my solutions. Obviously it may cause some problems.
onPress={() => {
setSkip((prev) => !prev);
while(isFetching){}
// save data
}}
And with the code below, storeData will be called multiple times.
export function Login() {
const [skip, setSkip] = useState(true);
const { data, isFetching } = useVerifyUserQuery(userState, {
skip,
});
if (!isFetching && IsNotEmpty(data)){
storeData();
}
const LoginButton = () => (
<Button
title="Login"
onPress={() => {
setSkip((prev) => !prev);
}}
/>
);
return (
…
)
}
It looks like you just want to use the lazy version - useLazyVerifyUserQuery instead of common. It will be like:
export function Login() {
const [ verifyUser ] = useLazyVerifyUserQuery();
const handleLogin = async () => {
const data = await verifyUser(userState).unwrap();
// Probably you would want to use `storeData` somehow here?
}
const LoginButton = () => (
<Button
title="Login"
onPress={handleLogin}
/>
);
return (
...
)
}
PS: just a warning - using a nested component definition, like LoginButton inside Login - is a known antipattern that may cause significant performance issues.
I'm building a project overview app and I'm using React-Native-calendar. I also created two buttons to filter the calendar. I'm fetching the data(API), I'm mapping the data to an object for "markedDates". Everything works appropriately as it should. Now the onPress of each button assigns the object to a state to filter. That works as well. What doesn't work is that those markedDates, that for sure come in correctly, are not shown when the app loads. They are shown however when I click on a button, but not on load. The rough code order:
const [meineTermine, setMeineTermine] = useState([]);
const [dates, setdates] = useState([]);
const [markedFinal, setMarkedFinal] = useState({});
useEffect(() => {
const unsubscribe = db.collection("Dates").onSnapshot(snapshot => (
setdates(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
})))
))
const filter = db.collection("Dates").where("involv", "==", auth.currentUser.displayName).onSnapshot(snapshot => (
setMeineTermine(
snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
})))
))
return unsubscribe && filter;
}, [])
let markedDayAll = {};
let markedDayMe = {};
{dates.map(({data: {anfang}}) => (
markedDayAll[anfang] = {
selected: true
}
))}
{meineTermine.map(({data: {anfang}}) => (
markedDayMe[anfang] = {
selected: true
}
))}
<View>
<Button onPress={setMarkedFinal(Object.assign({}, markedDayAll))}/>
<Button onPress={setMarkedFinal(Object.assign({}, markedDayMe))}/>
<CalendarList
markedDates={ markedFinal }
onDayPress={() => navigation.navigate("ViewDate")}
/>
</View>
I tried to map the data to objects in useEffect which didn't work. I also tried to have a default value in state, which also didn't work. What am I missing? In which order do I need to set the code up, since it works, just not on load. Where do I need to implement the "setMarkedFinal" so it shows on load?
I'm trying to optimize my code with hooks. I am thinking to move all bottom sheet refs into a useBottomSheet hook so I can share those refs and be able to manipulate the bottom sheet from any components that import the refs, or callbacks that use those refs. SO I have this:
export const useBottomSheet = () => {
const searchModalRef = useRef<BottomSheetModal>(null);
const handleOpenFilters = useCallback(() => {
console.log('GO');
searchModalRef.current?.snapToIndex(0);
}, []);
In my screen I have
const SearchScreen = () => {
const { searchModalRef } = useBottomSheet();
return (
<>
<Button onPress={() => searchModalRef.current?.snapToIndex(0)} title="PRESS" />
<BottomSheet
ref={searchModalRef}
...
/>
When I press the button, the BottomSheet moves. But when I import const { handleOpenFilters } = useBottomSheet(); in another component and use it, I can see it prints "GO" in the console, but the bottomsheet doesn't move. How come?
It looks like you forgot to return the values you destructure when you call the hook!
export const useBottomSheet = () => {
const searchModalRef = useRef<BottomSheetModal>(null);
const handleOpenFilters = useCallback(() => {
console.log('GO');
return searchModalRef.current?.snapToIndex(0);
}, []);
// add this:
return { searchModalRef, handleOpenFilters }
}
in my application, after receiving the updated data from the API I use useState, but this does not reflect on the information on the screen, I need to goBack and forward to change the information.
When I click on the button I save the information and on the return I need to update that the task has already been done, showing an "OK", but this is not updated, even though the "schedules" variable is right.
Where am I going wrong? What do I need to do to "return" and run again?
Thanks a lot!
import React, { useState, useEffect, useCallback } from "react";
...
const Pdvs = () => {
const [scheduletasks, setScheduletasks] = useState([]);
...
onSave = async (id) => {.....
const responseTask = await api.post("/schedules/fulldetails",{id});
setScheduletasks(responseTask.data);
...
return (
<Container>
{scheduletasks.map((keys) => (
{keys.done ? "OK"
) : ""}
<Button title="Done"
onPress={() =>
handleSave(keys.id)
}
/>
Your codes a little broken, especially in return(). Please revise it to get better help.
Sorry, I put just a piece of the code, to filter where is not the problem. Below more compleat code:
My "onSubmit" should be where you update the variable for useState
The big problem is in the Return, variable {keys.done}, is just that I want! :)
Thanks a lot
onSubmit = async (inputText) => {
try {
const { schedule_id, product_id, task_id } = scheduletasksResult;
const gpsPosition = await getLocationAsync();
const response = await api.post(`/scheduletasks/add`, {
schedule_id,
product_id,
task_id,
result: inputText,
gpsPosition,
});
if (response.status === 200) {
setScheduletasksID(response.data.id);
} else {
setScheduletasksID(0);
}
return response.data.id;
} catch (error) {
console.log(error.response.data);
}
};
useEffect(() => {
async function loadPdvs() {
setCompany_id(company_id[1]);
const response = await api.post("/schedules", {
company_id: company_id[1],
id,
});
setSchedules(response.data[0]);
const responseTask = await api.post("/schedules/fulldetails", {
company_id: company_id[1],
id,
});
setScheduletasks(responseTask.data);
}
loadPdvs();
}, []);
return (
<Container>
<RouteTitle>{schedules.tradeName}</RouteTitle>
<ScrollView>
{scheduletasks.map((keys) => (
<RoutesContainer key={keys.task_id}>
<RouteDetail>
{keys.task_name}
{keys.done ? (
<FontAwesomeIcon
icon={faCheckCircle}
/>
) : null}
</RouteDetail>
{keys.textRequired ? (
<Button
title="Observações"
onPress={() =>
showDialog(keys.schedule_id, keys.product_id, keys.task_id)
}
/>
) : null}
</RoutesContainer>
))}
</ScrollView>
</Container>
);