React-Native FlatList item clickable with data to another screen - react-native

I'm trying to access a screen when you click on an item in my flatlist by passing the date I retrieved from the firebase before, I've tried several things without success so I come to you.
Basically when I click on one of the elements -> A screen with details should appear.
export default function Notifications() {
const dbh = firebase.firestore();
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [deliveries, setDeliveries] = useState([]); // Initial empty array of users
useEffect(() => {
const subscriber = dbh
.collection("deliveries")
.onSnapshot((querySnapshot) => {
const deliveries = [];
querySnapshot.forEach((documentSnapshot) => {
deliveries.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setDeliveries(deliveries);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
style={{ flex: 1 }}
data={deliveries}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => { * HERE I NEED TO PASS DATA AND SHOW AN ANOTHER SCREEN FOR DETAILS * }}>
<View style={styles.container}>
<Text>DATE: {item.when}</Text>
<Text>ZIP DONATEUR: {item.zip_donator}</Text>
<Text>ZIP BENEFICIAIRE: {item.zip_tob_deliv}</Text>
</View>
</TouchableOpacity>
)}
/>
);
}
EDIT: Small precision this screen is located in a Tab.Navigator

you can pass params in navigation,
export default function Notifications(props) {
const { navigation } = props
const dbh = firebase.firestore();
const [loading, setLoading] = useState(true); // Set loading to true on component mount
const [deliveries, setDeliveries] = useState([]); // Initial empty array of users
useEffect(() => {
const subscriber = dbh
.collection("deliveries")
.onSnapshot((querySnapshot) => {
const deliveries = [];
querySnapshot.forEach((documentSnapshot) => {
deliveries.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setDeliveries(deliveries);
setLoading(false);
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
if (loading) {
return <ActivityIndicator />;
}
return (
<FlatList
style={{ flex: 1 }}
data={deliveries}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => {
navigation.navigate('screenName', {
//pass params here
})
}}>
<View style={styles.container}>
<Text>DATE: {item.when}</Text>
<Text>ZIP DONATEUR: {item.zip_donator}</Text>
<Text>ZIP BENEFICIAIRE: {item.zip_tob_deliv}</Text>
</View>
</TouchableOpacity>
)}
/>
);
}
you can access params in the navigated screen by props.route.params

Related

How to use asyncStorage inside useEffect

I'm building a mobile game using react native and I'm trying to retrieve the best value storage on it to display on the screen. The problem is that it seems that react native is rendering the screen before it retrieves the value and then it doesn't re-render when the value is updated using setBest(), so no value is displayed.
Here is the code:
const navigation = useNavigation()
const [result, setResult] = useState('')
const [best, setBest] = useState('')
useEffect(() => {
const Storage = async (key,value) => {
await AsyncStorage.setItem(key,value)
}
const Retrieve = async (key) => {
const value = await AsyncStorage.getItem(key)
setBest(()=>value)
}
Retrieve('1').catch(console.error)
setResult(route.params.paramKey)
if(route.params.paramKey>best){
var aux = result.toString()
Storage('1',aux)
console.log(best)
}
}, [])
return (
<View style={styles.container}>
<View style={styles.textView}>
<Text style={styles.tituloText}>Melhor pontuação</Text>
<Text style={styles.tituloText}>{best}</Text>
<Text style={styles.tituloText}>Sua pontuação</Text>
<Text style={styles.resultText}>{result}</Text>
<View style={styles.viewBtn}>
<TouchableOpacity style={styles.viewBack} onPress={() => navigation.navigate('Modo1')}>
<Icon style={styles.iconBack} name="backward" />
</TouchableOpacity>
<TouchableOpacity style={styles.viewHome} onPress={() => navigation.dispatch(StackActions.popToTop)}>
<Icon style={styles.iconBack} name="home" />
</TouchableOpacity>
</View>
</View>
</View>
);
}
Thanks for the help guys! I've been struggling with this for days and any help will be appreciated!
This is how you retrieve the value..
useEffect(() => {
AsyncStorage.getItem('key').then(value => {
if (value != null) {
console.log(value);
setBest(value);
}
});
}, []);
also don't forget to add the import statement..
To set the value you must use
AsyncStorage.setItem('key', value);
You can use Async Functions inside of ~useEffect()` like this:
useEffect(() => {
(async () => {
async function getData() {
try {
const value = await AsyncStorage.getItem('myKey');
if (value !== null) {
setData(value);
}
} catch (error) {
console.log(error);
}
}
getData();
})();
}, []);
}

How to re-render a flatlist

I'm making a mobile app that shows a list of movies, but when I search for a movie FlatList won't update, how can I fix it?
I tried too many things but it still does not work, my objective is to update the list when the button is pressed, the API gives me the data correctly but the list does not update.
This is my code:
export const Home = () => {
let { peliculasList, loadPeliculas } = peliculasPaginated();
const [name, setName] = useState('');
const [year, setYear] = useState('');
const [buscado, setBuscado] = useState(false);
const handleClick = async () => {
const resp = await peliculasApi.get<SimplePelicula[]>(`http://www.omdbapi.com/?t=${name}&y=${year}&plot=full&apikey=d713e8aa`);
setBuscado(!buscado);
peliculasList = resp.data
}
return (
<>
<View
style={{
alignItems: 'center',
height: 760
}}
>
<Text style={{
...style.title,
...style.globalMargin,
top: 0,
marginBottom: 0
}}>Movies</Text>
<TextInput
placeholder='Movie Name'
style={styles.input}
onChangeText={(val) => setName(val)}
/>
<TextInput
placeholder='Year'
style={styles.inputMovie}
onChangeText={(val) => setYear(val)}
/>
<TouchableOpacity onPress={() => handleClick()}>
<ButtonSr></ButtonSr>
</TouchableOpacity>
<FlatList
data={ peliculasList }
keyExtractor={ (pelicula) => pelicula.imdbID }
showsVerticalScrollIndicator={ false }
extraData={ buscado }
renderItem={({ item }) => ( <PeliculasCard pelicula={item} ></PeliculasCard> )}
/>
</View>
</>
)
}
Try to save your resp.data within the state and declare that state in your Flatlist's data prop may solve the issue.
Try this out just change the 'MOVIENAME' to a response from the api such as the movie name and refrence it as the item.API object of your choice
import React, { useState } from 'react'
import { View, Text, TextInput, FlatList, Button } from 'react-native'
export default function App() {
const [FetchedData, setFetchedData] = useState([])
const [SearchTerm, setSearchTerm] = useState('')
const [Data, setData] = useState(FetchedData)
const [ArrayHolder, setArrayHolder] = useState(FetchedData)
const FetchMovies = () => {
fetch('url')
.then(res => res.json())
.then(res => setFetchedData(res))
}
FetchMovies()
function dataSearch(text) {
const newData = ArrayHolder.filter(item => {
const itemData = item.MOVIENAME.toUpperCase()
const textData = text.toUpperCase()
return itemData.indexOf(textData) > -1
})
setData(newData)
}
return (
<View>
<Button title='Press' onPress={() => dataSearch(SearchTerm)} />
<TextInput
onChangeText={(text) => setSearchTerm(text)}
placeholder="Search Here"
/>
<FlatList
data={Data}
renderItem={({ item }) => <Text>{item.MOVIENAME}</Text>}
/>
</View>
)
}

Need some help handling the response I'm getting from json server

I'm developing an app in React Native in which I'm trying to display some data provided by a fake API I set up using json server. I'm using the useContext hook to handle the general state of the app and since I'm fairly new to React Native and React in general I need some help handling the response I'm manipulating through the context API.
This is the State file I set up in the context folder
import React, { useReducer } from 'react'
import MenusReducer from './MenusReducer'
import MenusContext from './MenusContext'
import { baseUrl } from '../../shared/baseURL'
const MenusState = (props) => {
const initialState = {
menus: [],
selectedMenu: null
}
const [state, dispatch] = useReducer(MenusReducer, initialState)
const getMenus = async () => {
const response = await fetch(baseUrl + 'RESTAURANTES')
const data = await response.json()
console.log('This is the reducer working'); // This is a test log to see if it works
dispatch({
type: 'GET_MENUS',
payload: data
})
}
const getDetails = async (id) => {
const response = await fetch(`${baseUrl}RESTAURANTES/${id}`)
const data = await response.json()
dispatch({
type: 'GET_DETAILS',
payload: data
})
}
return (
<MenusContext.Provider value={{
menus: state.menus,
selectedMenu: state.selectedMenu,
getMenus,
getDetails
}}>
{props.children}
</MenusContext.Provider>
)
}
export default MenusState;
So here I set up a getMenus() function by which I get all the items I'd like to display in my components. As you can see, I put a test log inside the function to see if it works, which it does.
The problem comes when I try to get those items inside my app components. Here's one of the instances in which I try to get the items to display.
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [response, setResponse] = useState([])
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
setLoading(false);
setResponse(data)
console.log(response);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{response[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}
So inside one of the ScrollViews I'm setting up a test to see if the response can be displayed, which it is not. However, inside the useEffect, I'm setting up a test log with the message 'This is the app executing' which is working, BUT, the response being logged is an empty array.
I'm sure the problem I'm facing has something to do with the asynchronous response between app and server, but I have no clear idea as to how I can address this.
Can someone please point me in the right direction? Thanks in advance!!
Based on your code, I think you can do this
const Home = ({ navigation }) => {
const { menus, getMenus } = useContext(MenusContext)
const [search, setSearch] = useState('')
const [categories, setCategories] = useState(allCategories)
const [loading, setLoading] = useState(true)
useEffect(() => {
const data = async () => await getMenus();
console.log('This is the app executing');
data();
setLoading(false);
}, [])
// ... some code later
return (
<ScrollView style={styles.yScroll}>
<View>
<Text style={styles.sectionTitle}>Destacados</Text>
</View>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={styles.sectionContainer}>
<Text>{menus[0]}</Text> // Here's where I'm trying to print something about the response but it's not working
</View>
</ScrollView>
<View>
<Text style={styles.sectionTitle}>Categorias</Text>
</View>
<View style={styles.sectionContainer}>
{categories.map((item, index) => {
return (
<View key={index} style={styles.category}>
<Text>{item}</Text>
</View>
)
})}
</View>
</ScrollView>
)
}

Show ActivityIndicator in React Native?

I have a function to fetch items from an API that is inside UseEffect. And i'm looking to call this function every time the status of the selectedItem or the items changes and show an ActivityIndicator before the function returns the result. The ActivityIndicator appears when the items are uploading but not when the status of the selectedItem changes ?
I have my code like this :
export default () => {
const [items, setItems] = useState();
const [selectedItem, setSelectedItem] = useState(null);
const [isLoading, setLoading] = useState(true);
const getItems = () => {
get('/api/items').then((rep) => {
setItems(rep);
setLoading(false);
}
});
};
useEffect(() => {
getItems();
}, [selectedItem.status]);
return (
<SafeAreaView style={styles.container}>
{isLoading ? (
<View style={[styles.spinnerContainer, styles.horizontal]}>
<ActivityIndicator />
</View>
) : ((items !== [])
&& (
<SectionList
stickySectionHeadersEnabled={false}
style={{ paddingHorizontal: 20, }}
sections={items}
refreshing={isLoading}
keyExtractor={(item, index) => item + index}
...
/>
))}
</SafeAreaView>
);
};
You can try setLoading(true) inside getItems
const getItems = () => {
setLoading(true);
get('/api/items').then((rep) => {
setItems(rep);
setLoading(false);
});
};

you need to specify name or key when calling navigate with an object as the argument

i'm having an messages screen and i need to navigate to a "single message" when tapping to the List item of messages but i get this error "you need to specify name or key when calling navigate with an object as the argument"
i have created the "single message" screen and added it as a <Stack.Screen/> also but i don't know what i'm doing wrong.
below is my code:
function MessagesScreen({navigation}) {
const [messages, setMessages] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const loadMessages = async () => {
const response = await messagesApi.getMessages();
setMessages(response.data);
}
useEffect(() => {
loadMessages();
}, []);
const handleDelete = message => {
setMessages(messages.filter((m) => m.id !== message.id));
}
return (
<Screen>
<FlatList
data={messages}
keyExtractor={message => message.id.toString()}
renderItem={({ item }) =>
<ListItem
title={item.fromUserId}
subTitle={item.content}
image={item.image}
onPress={() => navigation.navigate(routes.MESSAGE_SINGLE, item)}
renderRightActions={() =>
<ListItemDeleteAction onPress={() => handleDelete(item)} />}
/>
}
ItemSeparatorComponent={ListItemSeparator}
refreshing={refreshing}
onRefresh={() => {
setMessages([
{
id: 1,
title: 'T1',
description: 'D1',
image: require('../assets/mosh.jpg')
},
])
//setMessages(loadMessages());
}}
/>
</Screen>
);
}
const styles = StyleSheet.create({
})
export default MessagesScreen;
when i'm logging the "onPress" event on the console like this:
onPress={() => console.log('message selected', item)}
heres what i get:
and below is the MessageSingle screen i created to render the message but i dont know how to do it.
function MessageSingle() {
return (
<Screen>
<View style={styles.container}>
<AppText>{"kjhkjhjk"}</AppText>
{/* <AppText>{getMessagesApi}</AppText> */}
</View>
</Screen>
);
}
const styles = StyleSheet.create({
container: {}
});
export default MessageSingle;
so i want to get the message from the list of the messages. maybe i dont have to create e separate screen? i'm a beginner on this
any help would be appreciated!
you need to first add your MessageSingle component to the navigation container. Just put it as one of the screens along your MessagesScreencomponent. Then you need to navigate to it using that name:
onPress={() => navigation.navigate('MessageSingle', {item})}
the above will navigate to the screen with name MessageSingle, and passing the object item as a param.
in order to access this in your MessageSingle component, you need to use the route props.
function MessageSingle({route}) {
console.log('item = ', route.params?.item); // this would be your item.
return (
<Screen>
<View style={styles.container}>
<AppText>{"kjhkjhjk"}</AppText>
{/* <AppText>{getMessagesApi}</AppText> */}
</View>
</Screen>
);
}