This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Closed 1 year ago.
I am trying to set the state to the props I get from Redux. I have an array, props.feed, displaying an array on the console. But setting my state to this array doesn't change the state, that remains undefined, and it's not rendering my Flatlist because the data is undefined.
If I log props.feed it works. If I log my state posts after using setPosts the hook doesn't take the data.
function FeedScreen(props) {
const [posts, setPosts] = useState([]);
const imageWidth = Math.floor(useWindowDimensions().width);
useEffect(() => {
if (
props.usersFollowingLoaded == props.following.length &&
props.following.length != 0
) {
props.feed.sort((x, y) => {
return x.creation - y.creation;
});
console.log("Props Feed", props.feed);
setPosts(props.feed);
console.log("Posts of friends", posts);
}
}, [props.usersFollowingLoaded, props.feed]);
return (
<View style={styles.container}>
<View style={styles.containerGallery}>
<FlatList
numColumns={1}
data={posts}
horizontal={false}
renderItem={({ item }) => (
<View style={styles.containerImage}>
<Text style={styles.container}>
{item.user.name}
</Text>
<Image
style={styles.image}
source={{ uri: item.downloadURL }}
/>
<Text
onPress={() =>
props.navigation.navigate("Comments", {
postId: item.id,
uid: item.user.uid,
})
}
>
View comments...
</Text>
</View>
)}
/>
</View>
</View>
);
}
Here is the console.
You are misunderstanding on how React.useState (and rendering) works.
When you call setPosts, it doesn't immediately set posts to the value. It tells React "I want posts to equal <this value>" and React will re-render your component later with the updated state variable.
As for your FlatList not rendering the list, that's another issue. Is it a component you wrote yourself?
Related
const [number,setNum] = useState(0); I get this error when I want to add and change it(setNum(number+1)). My Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. What can i to solve this?
const App = ()=>{
const [text,setText] = useState('');
const [todo,setToDo] = useState([]);
const [number,setNum] = useState(0);
const renderToDoCard = ({item})=>{
setNum(number+1)
return(
<TouchableHighlight
onLongPress={() => handleLongPress(item)}>
<ToDoCard todo={item} number={number}/>
</TouchableHighlight>
)
}
const handleLongPress = item => {
setToDo(todo.filter(i => i !== item));
return Alert.alert('Silindi');
};
return(
<SafeAreaView style={styles.container}>
<StatusBar backgroundColor='#102027'/>
<View style={styles.head_container}>
<Text style={styles.title}>Yapılacaklar</Text>
<Text style={styles.title}>{todo.length}</Text>
</View>
<View style={styles.body_container}>
<FlatList data={todo} renderItem={renderToDoCard} />
</View>
<View style={styles.bottom_container}>
<ToDoInput todo={todo} setToDo={setToDo} text={text} setText={setText}/>
</View>
</SafeAreaView>
)
}
You've created an infinite update loop.
The problem is in how you're updating your number state inside renderToDoCard
const renderToDoCard = ({item}) => {
setNum(number + 1); // This is the problem, remove this line
return (
<TouchableHighlight onLongPress={() => handleLongPress(item)}>
<ToDoCard todo={item} number={number} />
</TouchableHighlight>
);
};
When renderToDoCard renders you update the state of your App component so it rerenders App which renders renderToDoCard which updates the state of your App component so it rerenders App which renders renderToDoCard...
This process repeats until the max update depth is reached.
Simply remove setNum(number + 1); and that problem is fixed.
It seems to me from your code that all you use your number state for is to keep track of the current item index so you can pass this to the ToDoCard component. The FlatList's renderItem also provides access to the current item index which you could pass to the number prop of ToDoCard
renderItem({ item, index, separators });
https://reactnative.dev/docs/flatlist#required-renderitem
So you could instead do something like this
const renderToDoCard = ({item, index}) => {
return (
<TouchableHighlight onLongPress={() => handleLongPress(item)}>
<ToDoCard todo={item} number={index} />
</TouchableHighlight>
);
};
Alternative you can add a key to each item in todo and use that instead of the index.
I am trying to fetch images stored on firebase storage and display in react native app. the functions works to get the download URL of images but it doesn't merge previous state and update component, it just displays in only one image (due to useState). so as each image is fetched it displays the image in the same image component instead of creating a new component to display in
Code:
import React, {setState, useState} from 'react';
import {DrawerActions, useNavigation} from '#react-navigation/native';
const Gallery = () => {
const navigation = useNavigation();
const [downloadUrl, setDownloadUrl] = useState({url: undefined});
console.log(`URL should be undefined at this point ${downloadUrl}`);
function listFilesAndDirectories(reference, pageToken) {
return reference.list({pageToken}).then(result => {
result.items.forEach(ref => {
ref.getDownloadURL().then(url => {
// console.log(`Image URL is:\n ${url}`);
setDownloadUrl({url: url});
});
});
if (result.nextPageToken) {
return listFilesAndDirectories(reference, result.nextPageToken);
}
return Promise.resolve();
});
}
const storageReference = firebase
.storage()
.refFromURL('gs://ab404.appspot.com/images');
listFilesAndDirectories(storageReference).then(() => {
console.log('Started listing image download urls');
});
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<View style={SharedStyles.header}>
<TouchableOpacity
onPress={() => {
navigation.dispatch(DrawerActions.openDrawer());
}}>
<Hamburg />
</TouchableOpacity>
<Header title="Gallery" />
</View>
<ScrollView contentInsetAdjustmentBehavior="automatic">
<View style={styles.container}>
<View style={styles.sectionContainer}>
<View>
<Text>
Welcome to the Abulad Gallery! Come back for more content....
</Text>
<Image
source={{uri: downloadUrl.url}}
style={styles.fetchedImage}
/>
</View>
</View>
</View>
</ScrollView>
</SafeAreaView>
</>
);
};
I have tried using the spread operator to get previous state and update as needed but I get an invalid attempt to spread a non-iterable instance
I need a way to make images appear in their component while updating url
Thanks
This is my function for rendering items in a flatlist
renderItem = ({ item }) => {
var integer = Number(item.key)
return (
<View>
<Text style={styles.row}>
{item.text}
</Text>
<View style={{flexDirection:'row'}}>
{this.createButtonYes(integer)}
{this.createButtonNo(integer)}
{this.answer(this.state.buttonStates[integer])}
</View>
<Text >__</Text>
</View>
)
}
And the problem I am facing is the function this.answer is not being called when the state of buttonStates changes
answer = (val) => {
if(val){
return(
<Text style={{fontSize:20}}>YES</Text>
)
}else if(val==false){
return(
<Text style={{fontSize:20}}>NO</Text>
)
}else{
return(
<Text style={{fontSize:20}}>Not Answered</Text>
)
}
}
I assumed that every time the state changes the function would be called but that does not seem to be the case, so does anyone have a solution? What I want is whenever the buttons are pressed the state will change and then this.answer will take the changed state and display what it has to accordingly.
Thanks
EDIT:
Code for the button:
buttonYesHelp = num =>{
const newItems = [...this.state.buttonStates];
newItems[num] = true;
return newItems
}
createButtonYes = (num) => {
return(
<TouchableOpacity style={styles.buttonYes}
onPress =
{
()=> {{this.setState({ buttonStates:this.buttonYesHelp(num) })}}
}>
<Text style={styles.buttonTextStyle}>YES</Text>
</TouchableOpacity>
)
}
num is the index of the thing I want to change in the list
EDIT:
I have tried multiple different things but the problem I keep running into is that when I render the button I want it to react to a state variable but it never seems to change based on the state even when the state is changing.
For example, in this.answer I assumed that it would return the text based on the state of buttonStates but it seems to only account for the initial state and nothing after
I was able to achieve this in a different piece of code with identical syntax but for some reason this is not working
I am still having trouble understanding ref's in React Native (and React in general). I am using functional component. I have a FlatList that has many items. How do I create a reference for a thing within an item like a Text or View component?
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => 'text' + item.id = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
Then in _editItem I want to reference the Text component so that I can change its text from 'EDIT' to 'EDITING', or even change its style, or whatever.
_editPost = id => {
console.log(text + id)
}
I have tried...
FeedComponent = () => {
let editPosts = {}
<FlatList
data={data}
renderItem={({ item }} => {
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => _editITem(item.id)}>
<Text ref={(a) => editPosts[item.id] = a}>EDIT</Text>
</TouchableOpacity>
</View>
}
/>
...and a few other things, but I think I might be way off so I could use some guidance.
Typically you don't use refs in react to update content like text. Content should be rendered based on the current props and state of your component.
In the case you describe you'll probably want to set some state in the parent component that then impacts the rendering of the item.
As a sidenote refs are used if you need to trigger a method on a child component like calling focus on a TextInput for example but not for imperatively updating component content.
In your case you'll want to update some state representing the current active item. Something like:
import React, {useState} from 'react';
FeedComponent = () => {
const [activeItem, setActiveItem] = useState(null);
<FlatList
data={data}
renderItem={({ item }} => {
return (
<View>
... lots of other stuff here
<TouchableOpacity onPress={() => setActiveItem(item.id)}>
{activeItem === item.id
? <Text>EDITING</Text>
: <Text>EDIT</Text>
}
</TouchableOpacity>
</View>
);
}
extraData={activeItem}
/>
I am trying to render a FlatList inside a component. The Component itself is inside a ScrollView.
I am using map function to loop through the data to pass into the component.
Earlier I was using ScrollView instead of FlatList. It was working fine, but was rendering slow. So I decided to use FlatList.
Here's my code:
renderComp(){
const { filtersView,cats,cats_title, clearStyle} = styles;
const data = this.props.ingreds;
const arr = Object.entries(data);
return arr.map(i=> {
const name= i[0];
const items_obj = i[1];
const items = Object.values(items_obj);
return(
<View key={name} style= {filtersView}>
<View style={cats}>
<Text style ={cats_title}>{name}</Text>
<Text style={clearStyle}>Clear All</Text>
</View>
<View style={{justifyContent:'flex-start', alignItems:'flex-start'}}>
<FlatList
style={{ marginRight:6}}
data={items}
keyExtractor={(x,i)=> i.toString()}
renderItem={({item}) =>{
this.renderItems(item)
}}
/>
</View>
</View>
)
})
}
And here's the ScrollView Component:
<ScrollView contentContainerStyle={{alignItems:'flex-start',
justifyContent:'flex-start',flex:1, height:72}} >
{this.renderComp()}
</ScrollView>
And The loop stops after one iteration.
Here's the output: https://i.stack.imgur.com/yM151.png
Any suggestions?
ReactNative FlatList renderItem method should return a ?React.Element component. In your case either use return this.renderItems or skip the inner brackets.
https://facebook.github.io/react-native/docs/flatlist#renderitem
({item}) => this.renderItems(item)}