Theres a screen where I show the cards, firstly it shows all the cards without filter and I'm trying to create his functionality of filter but shows this error, HELP ME PLS. The code is this:
const [preferenceSelected, setPreferenceSelected] = useState("");
const [list, setList] = useState(data);
useEffect(() =>{
if(preferenceSelected === ''){
setList(data);
}else{
setList(
data.filter(item => {
if(item.categoria === preferenceSelected){
console.log(list);
return true;
}else{
return false;
}
})
)
}
}), [preferenceSelected];
<View style={styles.EventCards}>
<Swiper
cards={list}
renderCard={(card) => {
return (
<TouchableOpacity style={styles.card} onPress={() => {
navigation.navigate('EventProfile', {
id: card.id,
nome: card.nome,
categoria: card.categoria,
image: card.image,
avaliacao: card.avaliacao,
});
}} >
<Image source={{uri:card.image}} style={styles.cardImg} />
</TouchableOpacity>
);
}}
stackSize={4}
stackSeparation={10}
disableBottomSwipe
disableTopSwipe
verticalSwipe={false}
backgroundColor={"transparent"}
keyExtractor={card => card.id}
></Swiper>
I tried to create his filter method but it always showed an error. And I'm using his dependency to utilize animated card.
I found myself getting this error frequently when trying to call objects in the frontend. I think this error occurs when the frontend loads before the information is called so here are my two workarounds.
To avoid this error I tried two things:
Firstly wrapping the element you're calling in an if statement in your frontend
{card != null ?
<>
//show frontend
</>
: null }
Another good option would be just to use an activity indicator
The other thing I tried was creating another useEffect listener as follows:
const [called, setCalled] = useState(false);
Then once the data is fetched, I set this value to true. Then at the end of my useEffect I added called in the square brackets as a listener.
What this does is refreshes the frontend once that variable changes. I hope this kind of helps
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} />
))}
I have a problem where useState updates the state but does not show the changes until I refresh the app. First, I declare an array called sampleFriends, made up of objects with fields "name", "location", and "picture" (each element of the array looks similar to: {name: 'John', location: 'Boston', picture: TestImage}).
Then, I have the following useState:
const [selectedFriends, setSelectedFriends] = useState([])
At some point, I successfully render
sampleFriends.map(({ name, location, image }, index) => (
<NewMsgTableRow
name={name}
index={index}
location={location}
image={image}
onPress={() => selectFriend(name)}
/>
))
And I also have this function right above
const selectFriend = name => {
// if the friend is not already selected
if (!selectedFriends.find(e => e === name)) {
const newFriends = selectedFriends
newFriends.push(name)
setSelectedFriends(newFriends)
}
}
The component NewMsgTableRow has a button that uses onPress
<TouchableOpacity
onPress={onPress}
>
So, I want to render selectedFriends as soon as they are selected (the TouchableOpacity is touched and thus the state updates). However, when I click the button, nothing shows up until I edit and save my code and it refreshes automatically. It was my understanding that useState rerendered the components as soon as it was updated, but it is not happening in this case and I can't figure out why. I've been reading that it is async and that it does not change it instantly, but I don't know how to make it work. Hope it makes sense and thanks for your help!!
You can use array spread or Array.concat() to make a shallow clone, and add new items as well) so change the line below:
const newFriends = selectedFriends
to this line :
const newFriends = [...selectedFriends]
I have a custom hook in my React application which uses a GET request to fetch some data from the MongoDB Database. In one of my components, I'm reusing the hook twice, each using different functions that make asynchronous API calls.
While I was looking at the database logs, I realized each of my GET requests were being called twice instead of once. As in, each of my hooks were called twice, making the number of API calls to be four instead of two. I'm not sure why that happens; I'm guessing the async calls result in re-renders that aren't concurrent, or there's somewhere in my component which is causing the re-render; not sure.
Here's what shows up on my MongoDB logs when I load a component:
I've tried passing an empty array to limit the amount of time it runs, however that prevents fetching on reload. Is there a way to adjust the custom hook to have the API call run only once for each hook?
Here is the custom hook which I'm using:
const useFetchMongoField = (user, id, fetchFunction) => {
const [hasFetched, setHasFetched] = useState(false);
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
if (!user) return;
try {
let result = await fetchFunction(user.email, id);
setData(result);
setHasFetched(true);
} catch (error) {
setError(error.message);
}
};
if (data === null) {
fetchData();
}
}, [user, id, fetchFunction, data]);
return { data, hasFetched, error };
};
This is one of the components where I'm re-using the custom hook twice. In this example, getPercentageRead and getNotes are the functions that are being called twice on MongoDB (two getPercentageRead calls and two getNotes calls), even though I tend to use each of them once.
const Book = ({ location }) => {
const { user } = useAuth0();
const isbn = queryString.parse(location.search).id;
const { data: book, hasFetched: fetchedBook } = useFetchGoogleBook(isbn);
const { data: read, hasFetched: fetchedPercentageRead } = useFetchMongoField(
user,
isbn,
getPercentageRead
);
const { data: notes, hasFetched: fetchedNotes } = useFetchMongoField(
user,
isbn,
getNotes
);
if (isbn === null) {
return <RedirectHome />;
}
return (
<Layout>
<Header header="Book" subheader="In your library" />
{fetchedBook && fetchedPercentageRead && (
<BookContainer
cover={book.cover}
title={book.title}
author={book.author}
date={book.date}
desc={book.desc}
category={book.category}
length={book.length}
avgRating={book.avgRating}
ratings={book.ratings}
language={book.language}
isbn={book.isbn}
username={user.email}
deleteButton={true}
redirectAfterDelete={"/"}
>
<ReadingProgress
percentage={read}
isbn={book.isbn}
user={user.email}
/>
</BookContainer>
)}
{!fetchedBook && (
<Wrapper minHeight="50vh">
<Loading
minHeight="30vh"
src={LoadingIcon}
alt="Loading icon"
className="rotating"
/>
</Wrapper>
)}
<Header header="Notes" subheader="All your notes on this book">
<AddNoteButton
to="/add-note"
state={{
isbn: isbn,
user: user,
}}
>
<AddIcon color="#6b6b6b" />
Add Note
</AddNoteButton>
</Header>
{fetchedNotes && (
<NoteContainer>
{notes.map((note) => {
return (
<NoteBlock
title={note.noteTitle}
date={note.date}
key={note._noteID}
noteID={note._noteID}
bookID={isbn}
/>
);
})}
{notes.length === 0 && (
<NoNotesMessage>
You don't have any notes for this book yet.
</NoNotesMessage>
)}
</NoteContainer>
)}
</Layout>
);
};
The way you have written your fetch functionality in your custom hook useFetchMongoField you have no flag to indicate that a request was already issued and you are currently just waiting for the response. So whenever any property in your useEffect dependency array changes, your request will be issued a second time, or a third time, or more. As long as no response came back.
You can just set a bool flag when you start to send a request, and check that flag in your useEffect before sending a request.
It may be the case that user and isbn are not set initially, and when they are set they each will trigger a re-render, and will trigger a re-evalution of your hook and will trigger your useEffect.
I was able to fix this issue.
The problem was I was assuming the user object was remaining the same across renders, but some of its properties did in fact change. I was only interested in checking the email property of this object which doesn't change, so I only passed user?.email to the dependency array which solved the problem.
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 a Flatlist which works like a To Do list with a filter for “ToDo” and “Upcoming”. When a user swipes to complete the item, it gets hidden from the list by changing a displayIndex attribute. I would like this to reload the list after the swipe or before the user selects “Upcoming”. After reading through other stack overflow answers I have tried adding extraData={this.state} (and creating a this.state.refresh property which changes after every swipe) to the Flatlist and I also ensured that the list items themselves are React.Components and not PureComponents. I have also tried two ways to hide the ListItems, conditionally rendering them and conditionally changing the style to hidden. Still, I am not seeing any change in my Flatlist.
Below is some partial code to see if there are any gotchas I missed:
In the MainScreen.js
async _addCompletion(myItem) {
//Lots of business logic and after it's done the below code activates
await AsyncStorage.setItem(myItem.key, JSON.stringify(myItem));
await this._updateData();
this.setState({ refresh: !this.state.refresh });
}
render() {
const buttons = ['To Do', 'Upcoming'];
const { displayModeIndex } = this.state;
return (
<View>
<ButtonGroup
onPress={this._updateButtonIndex}
buttons={buttons}
selectedIndex={displayModeIndex}
/>
<FlatList
displayMode={this.state.displayModeIndex}
data={this.state.data}
extraData={this.state}
scrollEnabled={this.state.scrollEnabled}
renderItem={({ item }) => (
<MyListItem
myListItem={item}
addCompletion={this._addCompletion}
displayIndex={this.state.displayModeIndex}
setScrollEnabled={this._setScrollEnabled}
navigation={this.props.navigation}
/>
)}
/>
</View>
);
}
In MyListItem.js
_displayMyItem {
//Logic that determines whether to display a myItem based on several factors. I can confirm this works after refreshing.
}
_hideMyItem = () => {
Animated.timing(this.containerHeight, {
toValue: 0,
}).start(() => {
this.setState({ hidden: true });
});
};
render () {
const {myItem} = this.state;
//Other code that determines how the list item looks depending on myItem data.
return (
//I have also tried to return null if this._displayMyItem(this.state.myItem) returns false
<View style={!this._displayMyItem(this.state.myItem) && { display: 'none' }}>
<Swipeable
onPress={this._onPressRow}
setScrollEnabled={this.props.setScrollEnabled}
addCompletion={this.props.addCompletion}
hideMyItem={this._hideMyItem}
myItem={this.state.myItem}
>
//Other JSX Code
</View>
)
}
The Swipeable is a custom component that calls addCompletion after a swipe and _hideMyItem after everything is done. It is not a PureComponent either.
There's a lot going on here, so I've only included code that seems relevant. I can add more if needed. The addCompletion method is a long
would help some captures...
When you swipe the item , it's just empty right?, if it leaves an empty space try this way of conditional rendering , idk if it would work.
in MyListItem.js
render () {
const {myItem} = this.state;
//Other code that determines how the list item looks depending on myItem data.
return (
//I have also tried to return null if this._displayMyItem(this.state.myItem) returns false
{!this.state.hidden?
<View style={!this._displayMyItem(this.state.myItem) && { display: 'none' }}>
<Swipeable
onPress={this._onPressRow}
setScrollEnabled={this.props.setScrollEnabled}
addCompletion={this.props.addCompletion}
hideMyItem={this._hideMyItem}
myItem={this.state.myItem}
>
//Other JSX Code
</View>:null}
)
}
wich checks if this.state.hidden is false , returns the component, else, returns null