What I'm trying to do is to display in a list all the clients.
When there is no client in the list, it is another view that is displayed which says no client exists.
The initial value of the state its an empty array.
So when the axios.get() method gets the data from the backend, two times its called the initial value(which is an empty array), and after its filled with the client list that comes from backend.
const [clientList, setClientList] = useState([])
useEffect(() => {
axios
.get(`api/${phone}`)
.then(setClientList(response.data.data))
})
.catch((error) => console.log(error));
}
});
}, [clientList]);
console.log(clientList)
return(
{clientList.length > 0? (
<View>
<FlatList
data={clientList}
renderItem={(item) => {
let client= item.item.fullName;
return (
<View >
<Text>{client}</Text>
/>
</View>
)
}}
/>
</View>
) : (
<Text> No Client List </Text>
)}
)
When I console the clientList it will show:
clientList[]
clientList[]
clientList[
{
fullName: John Doe
},
{
fullName: Nick Smith
}
]
The first two empty arrays (that comes from the initial value of the useState) it will show the No Client List every time the user goes to the client list screen or reload the screen.
How can I prevent showing the <Text> No Client List </Text> when the clientList has clients on the array?
you can add a new state isLoading which will be true by default to handle the clientList initial empty array case.
const [isLoading, setIsLoading] = useState(true)
...
useEffect(() => {
axios
.get(`api/${phone}`)
.then(() => {
setIsLoading(false)
setClientList(response.data.data)
})
.catch(() => {
setIsLoading(false)
});
}, []);
...
// you can conditionally return UI based on `isLoading` or you can use `ListEmptyComponent` prop from FlatList along with `isLoading`
if(isLoading) {
return(<Text>loading</Text>)
}
// remaining code
return(
...
Related
I have a notes state array that I have that stores the user's inputs. When a user initially opens this specific screen, the component should fetch the user's notes, and return that array or an empty one depending on if they have data or not, and display the notes on the screen if they exist. When they add a note, the component should push this new note to the notes array and call AsyncStorage.setItem to store the new array. The component should then re-render with the state variable changing.
When I run this code, though, nothing happens. My state does not seem to update, and even though I submit text, the screen does not re-render, nor does any new text appear in the section it is supposed to appear in. Anyone know where I went wrong?
UPDATE: I have added the full code block here
const [notes, setNotes] = React.useState(null);
let getNotes = async () => {
try {
let json = await AsyncStorage.getItem(`${id}-notes`);
if (json != null) {
setNotes(JSON.parse(json));
} else {
setNotes([]);
}
} catch (e) {
console.log(e);
}
}
React.useEffect(() => {
console.log(notes, '- Has changed');
getNotes();
}, [notes]);
// user input check
<TextInput
style={styles.text}
value={note}
onChangeText={text => {setNote(text)}}
onSubmitEditing={event => {
if (event.nativeEvent.text) {
setNotes([...notes, event.nativeEvent.text]);
AsyncStorage.setItem(`${id}-notes`, JSON.stringify(notes), (e) => {});
setVisible(false);
}
}}
multiline={true}
returnKeyType='go'
/>
// what i want the screen to render
{notes && notes.map(note => {
<Note note={note} />
})}
I don't think the problem is that the state is not being set,
I think the problem is that you aren't returning anything from you map function.
Either add the return keyword.
Like this:
{notes && notes.map(note => {
return <Note note={note} />
})}
Or simply remove the curly-brackets to turn the statement into an "implicit return".
Like this:
{notes && notes.map(note => (
<Note note={note} />
))}
so i want to pass a piece of state to a screen. when i console.log it, it is perfectly fine but when i update the state it does not dynamically update.
my home screen
useEffect(() => {
// SNAPSHOT USER DATA
db.collection("users").doc(currentUserUID)
.onSnapshot((doc) => {
setUserData(doc.data());
});
}, [])
than i pass it into
<FlatList
horizontal
style={{ marginBottom: 40 }}
data={trackscreenData.ModulePbs1}
showsHorizontalScrollIndicator={false}
keyExtractor={(item) => item.TrackId}
renderItem={({ item }) => {
return (
<TouchableOpacity onPress={() => {
navigation.navigate('Module', {
TrackId: item.TrackId,
userData: userData,
});
}}>
and than on my 'Module' screen i import te state
export const ModuleScreen = ({navigation, route}) => {
const userData = route.params.userData;
} console.log(userData)
return()
so when i update my state it updates on my homescreen but not in my Module screen.
what am i doing wrong?
Pass userData as params in route it doesn't make userData in Modulescreen changed if userData updated from Homescreen.
if you want to do this, you need to use State Management.
Can you confirm that the Flatlist is getting the updated data. For that put a console and make sure you were receiving the update inside the renderItem. If not try to add extraData property. Also if you are interested, go through the React Native Firebase doc.
I have a database listener on my app that updates my data state every time a user posts something. When I get the new post I update my state called "posts" which is an array with all the items that the FlatList renders.
The problem is that after updating the state I can't see the new post in the top of the list until I go down (to the footer of the list) and then back to the top.
My FlatList code is:
const keyExtractor = ({id}) => id
...
const renderItem = ({ item }) => {
const {
uri,
description,
location,
width,
height,
likes,
comments,
date,
} = item;
return (
<Card
uri={uri}
description={description}
location={location}
width={width}
height={height}
likes={likes}
date={date}
/>
);
};
return (
<FlatList
data={data} // data is the post state of the parent
renderItem={renderItem}
keyExtractor={keyExtractor}
initialNumToRender={15}
windowSize={WINDOW_HEIGHT * 2}
maxToRenderPerBatch={15}
updateCellsBatchingPeriod={50}
removeClippedSubviews={false}
ListFooterComponent={
isLoading ? (
<View style={styles.footer}>
<Loading type="ios" size={18} color={colors.gray} />
</View>
) : null
}
/>
);
}
And this is how I update the post state (in the parent component of the flat list)
function Posts(props) {
const [posts, setPosts] = useState([]);
useEffect(() => {
const { firebase } = props;
let postsArray = [];
// Realtime database listener
const unsuscribe = firebase
.getDatabase()
.collection("posts")
.doc(firebase.getCurrentUser().uid)
.collection("userPosts")
.orderBy("date") // Sorted by upload date
.onSnapshot((snapshot) => {
let changes = snapshot.docChanges();
changes.forEach((change) => {
if (change.type === "added") {
// Get the new post
const newPost = change.doc.data();
// Add the new post to the posts list
postsArray.unshift(newPost);
}
});
// Reversed order so that the last post is at the top of the list
setPosts(postsArray);
});
/* Pd: At the first time, this function will get all the user's posts */
return () => {
// Detach the listening agent
unsuscribe();
};
}, []);
return (
<View style={styles.container}>
<CardList data={posts} />
</View>
);
}
The issue could be caused by the way, you append new posts:
postsArray.unshift(newPost)
..
setPosts(postsArray)
It doesn't seem to affect the reference of postsArray, thus no state updates happen, no re-renders seen.
You may try, instead:
setPosts([...postsArray])
Assign a marker property extraData for telling the list to re-render (since it implements PureComponent). If any of your renderItem, Header, Footer, etc. functions depend on anything outside of the data prop, stick it here and treat it immutably.
Your update would be :
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
extraData={/* You need to create another state variable which also changes when a post is added*/}
/>
For more information visit here
Im new in ReactNative and I'm trying to take some data from here https://www.dystans.org/route.json?stops=Hamburg|Berlin
When I try console.log results it return full API response. I dont know why in first results.distance works and return distance, but when I'm trying to do it inside FlatList nothing is returned. Sometimes it works when i want to return only item.distance but can't somethnig like <Text>{item.stops[0].nearByCities[0].city}</Text> nowhere in my code also in console. Im getting error:
undefined is not an object (evaluating 'results.stops[0]')
imports...
const NewOrContinueScreen = ({ navigation }) => {
const [searchApi, results, errorMessage] = useDystans();
console.log(results.distance);
return (
<SafeAreaView forceInset={{ top: "always" }}>
<Text h3 style={styles.text}>
Distance: {results.distance}
</Text>
<Spacer />
<FlatList
extraData={true}
data={results}
renderItem={({ item }) => (
<Text>{item.distance}</Text>
// <Text>{item.stops[0].nearByCities[0].city}</Text>
)}
keyExtractor={item => item.distance}
/>
<Spacer />
</SafeAreaView>
);
};
const styles = StyleSheet.create({});
export default NewOrContinueScreen;
And here is my hook code:
import { useEffect, useState } from "react";
import dystans from "../api/dystans";
export default () => {
const [results, setResults] = useState([]);
const [errorMessage, setErrorMessage] = useState("");
const searchApi = async () => {
try {
const response = await dystans.get("route.json?stops=Hamburg|Berlin", {});
setResults(response.data);
} catch (err) {
setErrorMessage("Something went wrong with useDystans");
}
};
useEffect(() => {
searchApi();
}, []);
return [searchApi, results, errorMessage];
};
As the name implies, FlatList is designed to render a list. Your API endpoint returns a JSON Object, not an Array, so there's nothing for the FlatList to iterate. If you want to show all the stops in the list, try passing in the stops list directly.
<FlatList
data={results.stops}
renderItem={({ item }) => (<Text>{item.nearByCities[0].city}</Text>)}
/>
Some side notes: (1) The extraData parameter is used to indicate if the list should re-render when a variable other than data changes. I don't think you need it here at all, but even if you did, passing in true wouldn't have any effect, you need to pass it the name(s) of the variable(s). (2) The keyExtractor parameter is used to key the rendered items from a field inside of them. The stop objects from the API don't have a member called distance so what you had there won't work. From my quick look at the API response, I didn't see any unique IDs for the stops, so you're probably better off letting React key them from the index automatically.
I have populated a FlatList with data fetched from Google's firebase backend. The implementation is rather standard, here's a stripped down version:
export default class Day extends Component {
state = { data : [], today: false }
componentWillMount = async () => {
const { today } = this.state;
const { calendarDb } = this.props
await calendarDb.onNewAgenda({
day : today
, then: this.parseOnListed
})
}
parseOnListed = blob => {
const { data } = this.state;
data.push(blob)
this.setState({ data: data })
}
renderItem = ({ item }) =>
<Hour data = {item}/>
render = () =>
<FlatList
data = {this.state.data}
renderItem = {this.renderItem}
keyExtractor = {item => item.ID}
/>
}
The issue is that every time a new blob is pushed into data, the <Image/> component in <Hour data={item}/> flickers. This makes the list a no-go in terms of user experience. What gives? <Hour/> is standard as well, and more or less look like this:
const Hour = ({ data }) =>
<View>
<Image source={{uri:data.uri}}/>
<Text> {data.name} </Text>
</View>
The content of <Text> does not flicker, only the image from <Image .../>
Check whether keyExtractor is getting unique ID or not.
The flat list is re-rendering on state update and images are downloaded again. Because, each row is not uniquely identified as said in comments by #Guruparan Giritharan.
I found another reason that triggers this issue, of the FlatList flikering on React native. In my case, it happened every time I updated/changed the state of any function component. So, for instance, I was keeping the fetch results (data) and the next-page-id (for the next paginated fetch) in two separate function components:
const [data, setData] = useState([]);
const [pageId, setPageId] = useState(null);
Hence, every time would capture the results of my fetch, I would first set the data update and then the page id. It was the page id update what was causing the flicker.
const onEndReachedFetch = async () ={
fetch(pageId).then(result => {
setData(result.Data);
setPageId(result.pageId);
});
}
The fix was just to put the state data together so there is a single update instead. Then react is happy and doesn't flicker when adding new items to the list.
const onEndReachedFetch = async () ={
fetch(pageId).then(result => {
setResult(result);
});
}
Beware of any side states that you may be updating in the background, as they may also cause the flickering if they are triggered by anything on the FlatList.