React useState hooks not updating render Text - react-native

I'm using hooks to print a list of conversations, with the name of the contact and the last message for each.
I'm using an interval to check for each conversation if the last message has changed. It's working find with the API, I get the content and use setState to change it, but when I try to get it in the render part, I get the old one.
Here is some code:
const _getMessages = () => {
setDataMsg('');
if (dataContacts) {
dataContacts.map((itemValue, itemIndex) => {
ApiClient.instance.getConversation(itemValue.id).then(response => {
if (response.status === 200) {
setDataMsg([...dataMsg, response.data[response.data.length-1].content]);
console.log('getting all messages ', response.data[response.data.length-1].content);
}
}).catch(error => {
console.log(error);
Alert.alert('Erreur', 'Impossible de récupérer les messages')
})
})
}
else
clearInterval(intervalMsg);
if (props.navigation.state.routeName !== "MessagesPage") {
clearInterval(intervalMsg);
}
}
// Affichage du dernier message //
const _showMessage = (index) => {
if (dataMsg) {
return (dataMsg[index]);
}
return ('Aucun message');
}
And the render:
<FlatList
style={getStyle().messageList}
data={dataContacts}
extraData={dataMsg}
refreshing={true}
keyExtractor={(item) => {
return item.id.toString();
}}
renderItem={({item, index}) => {
return (
<TouchableOpacity
style={getStyle().card}
onPress={() => {
cardClickEventListener(item)
}}>
<View style={getStyle().cardImage}>
<Image style={getStyle().image}
source={_handleImg(item)}/>
</View>
<View style={getStyle().cardContent}>
<Text
style={[getStyle().name, {fontFamily: props.font}]}>{item.firstName + " " + item.lastName}</Text>
<Text
style={{fontFamily: props.font, fontStyle:'italic'}}>{_showMessage(index)}</Text>
</View>
<View style={getStyle().cardIndicator}>
</View>
</TouchableOpacity>
)
}}/>

Related

fetch API call in react native is not working when I load the screeen

I have made an App in react native. My app makes API calls to my webserver and then Displays information based on that. The problem Is when I first load this screen... I get the loading screen and the information is display in the way it is supposed to but when I leave the screen and then comeback to the screen, it shows nothing and my array containing the items is empty, hence I think I am having problems with the API call when I leave the screen and then come back.
I am using React navigation 5 in My App.
export default function ({ navigation }) {
const [openQueries, setOpenQueries] = useState([]);
const [isLoading, seIsLoading] = useState(true);
const open_queries = [];
function getOpenQueries() {
var retrieveData = async () => {
try {
var value = await AsyncStorage.getItem("user");
var data = JSON.parse(value);
return data.user._id;
} catch (error) {
alert(error);
}
};
retrieveData().then((user) => {
fetch(URL + "/api/contact/open_queries", {
method: "POST",
body: "user=" + user + "&status=open",
headers: { "Content-type": "application/x-www-form-urlencoded" },
})
.then((response) => {
return response.json();
})
.then((responseJson) => {
if (responseJson.error === null) {
setOpenQueries(responseJson.open_queries);
seIsLoading(false);
}
});
});
}
getOpenQueries();
openQueries.forEach((query) => {
open_queries.push(
<TouchableOpacity
onPress={() =>
navigation.navigate("Chat", {
id: query._id,
title: query.title,
query: query,
showInput: true,
})
}
>
<View style={styles.inboxItem}>
<Text style={styles.inboxTitle}>{query.title}</Text>
<Text style={styles.inboxSubtext}>
{query.chats[query.chats.length - 1].chat}
</Text>
<View style={styles.lineBreak}></View>
</View>
</TouchableOpacity>
);
});
return (
<SafeAreaView style={styles.container}>
<Text style={styles.title}>Contacts</Text>
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate("NewQuery")}
>
<Text style={styles.text}>Start a new query</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate("ClosedQueries")}>
<View style={styles.button}>
<Text style={styles.text}>Closed Queries</Text>
</View>
</TouchableOpacity>
<Text style={styles.subTitle}>Open Queries</Text>
{isLoading ? (
<View style={styles.loader}>
<Code />
</View>
) : (
<ScrollView style={{ paddingTop: 10 }}>
{openQueries.length > 0 ? (
open_queries
) : (
<Text style={styles.noQuery}>No Open Queries found</Text>
)}
</ScrollView>
)}
<ScrollView></ScrollView>
<BottomNavigation navigation={navigation} active={"contact"} />
</SafeAreaView>
);
}
Try this way
export default function ({ navigation }) {
const [openQueries, setOpenQueries] = useState([]);
const [isLoading, seIsLoading] = useState(true);
const [open_queries_views, setOpenQueriesViews] = useState([]);
function getOpenQueries() {
....
}
// Similar to componentDidMount
useEffect(() => {
getOpenQueries();
});
function renderViews(){
const open_queries = [];
openQueries.forEach((query) => {
open_queries.push(
<TouchableOpacity> ... </TouchableOpacity>
);
});
setOpenQueriesViews(open_queries); // set state here to auto reflect on view
}
return (
<SafeAreaView style={styles.container}>
....
<ScrollView style={{ paddingTop: 10 }}>
{open_queries_views.length > 0 ? (
open_queries_views
) : (
<Text style={styles.noQuery}>No Open Queries found</Text>
)}
</ScrollView>
.....
</SafeAreaView>
);
}

Component not re-rendering on useState array change

I have a favorite button on the 'tweet' card that I show on the FeedScreen.js.
~~~~~~~~~ IMPORTS SNIP ~~~~~~~~~
function FeedScreen(props) {
const [feed, setFeed] = useState([]);
const [favorites, setFavorite] = useState([]);
const [refreshing, setRefreshing] = useState(false);
useEffect(() => {
loadFeed(0, 4);
}, []);
const loadFeed = async (last_id = 0, limit = 1) => {
setRefreshing(true);
const response = await tweetsApi.getTweets(last_id, limit);
if (response.ok) {
setFeed(response.data["data"].concat(feed));
} else {
console.log(response.problem);
}
setRefreshing(false);
};
const handleBookmark = async (item_id) => {
const response = await tweetsApi.toggleBookmark(item_id);
if (response.ok) {
console.log("ok response");
setFavorite(favorites.concat(item_id));
// I've tried this as well
// setFavorite([...favorites].concat(item_id));
// but in vain
console.log(favorites);
}
};
return (
<Screen style={styles.screen}>
<FlatList
data={feed}
keyExtractor={(tweet) => {
return tweet.id.toString();
}}
renderItem={({ item }) => (
~~~~~~~~~ SNIP ~~~~~~~~~
<CardFooter
style={{ marginLeft: 20 }}
item={item}
onPress={handleBookmark}
/>
</View>
)}
ItemSeparatorComponent={ListItemSeparator}
refreshing={refreshing}
onRefresh={() => {
loadFeed(feed[0]["id"], 2);
}}
/>
</Screen>
);
}
~~~~~~~~~ SNIP ~~~~~~~~~
And here's the CardFooter.js :
~~~~~~~~~ SNIP ~~~~~~~~~
function CardFooter({ item, onPress }) {
return (
<View style={styles.bookmark}>
<TouchableOpacity
onPress={() => {
return onPress(item.id);
}}
>
{item.bookmarked && (
<FontAwesome name="bookmark" size={24} color="red" />
)}
{!item.bookmarked && (
<FontAwesome5 name="bookmark" size={24} color="black" />
)}
</TouchableOpacity>
</View>
</View>
);
}
export default CardFooter;
~~~~~~~~~ SNIP ~~~~~~~~~
However the component doesn't seem to re render.
I've looked at these :
react-component-not-re-rendering-after-using-usestate-hook
Similar here
Another one 17 days back - why-usestate-is-not-re-rendering
usestate-not-re-rendering-when-updating-nested-object
All of these and similar other ones, each one of them point to the fact that the a new array should be created so that react re-renders it.
Update
console.log output
yes the console.log is printing the array, although one value previous. That's because useState is async so it isn't printing the realtime array. So, when the second time this is called, it would show one item_id ( the previous one ) added to favorites
I finally solved this by managing the state in the component itself.
Not sure if this is 'the proper way' to do this, but read here (how-to-add-a-simple-toggle-function-in-react-native) that this is how you can do this.
So, now the bookmark component gets its response from the top level component ( FeedScreen.js ) :
const handleBookmark = async (item_id) => {
const response = await tweetsApi.toggleBookmark(item_id);
if (response.ok) {
return true;
} else {
return false;
}
};
And changing the CardFooter.js i.e. where the bookmark component resides.
function CardFooter({ item, onPress }) {
const [favorite, setFavorite] = useState(item.bookmarked);
return (
<View style={styles.bookmark}>
<TouchableOpacity
onPress={async () => {
let response = await onPress(item.id);
if (response) {
setFavorite(!favorite);
} else {
alert("Some error occurred");
}
}}
>
{favorite && <FontAwesome name="bookmark" size={24} color="red" />}
{!favorite && (
<FontAwesome5 name="bookmark" size={24} color="black" />
)}
</TouchableOpacity>
</View>
</View>
);
}
Concerns
I am a bit concerned about handling the response in this component.
Should I handle the async operation in the bottom component ?

i want to show a flat list data but not show some data especially image

I use this to collect data from my server
getPopularProduct = async () => {
const url = `https://swissmade.direct/wp-json/swissmade/home/popular`;
fetch(url)
.then(response => response.json())
.then((responseJson) => {
console.log(responseJson.products.thumbnail, "details")
if(responseJson.error == false){
this.setState({
dataSourcePopularProduct: responseJson.products,
//isLoading: false,
})
}
})
.catch((error) => {
console.log(error)
})
}
this the blank space imag and this is the flat list and render item
renderPopularProduct = ({ item }) => {
const entities = new Entities();
var id = item.pid;
var img = { 'uri': item.thumbnail };
return (
<TouchableOpacity onPress={() => this.props.navigation.navigate('ProductDetails', { id })}>
<View style={styles.GalleryBox}>
<View style={styles.GalleryImg} onPress={() => this.props.navigation.navigate("ProductDetails")}>
<Image source={img} style={styles.SingelImg} largeHeap="true"/>
</View>
<View style={styles.GalleryText}>
<Text style={styles.userNmae}>{item.title}</Text>
</View>
<View style={styles.amount}>
<Text style={styles.userNmae}>{entities.decode(item.currency)} {item.price}</Text>
</View>
</View>
</TouchableOpacity>
)
}
<FlatList
data={this.state.dataSourcePopularProduct}
renderItem={this.renderPopularProduct}
keyExtractor={(item, index) => index}
horizontal={true}
showsHorizontalScrollIndicator={false}
/>
and stylesheet
SingelImg: {
width: '150%',
height: '120%',
//resizeMode: 'cover',
marginLeft: -25
},
And this way I use flat list but when it shows some data are missing especially image. but my API returns every right data.

Using parameter that would fetch data from another api based on a condition react native?

I have two pages Portfolio.js and PortfolioDetails.js. In the Portfolio.js file, I am fetching data from my api and displaying all the portfolios in a list. When I click on portfolio, it should take me to the PortfolioDetails screen, which will display only those stocks from stock api which are in the portfolio.
e.g if I click on Portfolio with id 1, it should filter out stocks from stock api which has portfolio id 1 and display all those stocks on the screen.
So far, I am successful in fetching both the apis and also when I click on one portfolio, it passes the portfolio id parameter to my PortfolioDetails screen. I am stuck where I have to filter the stocks to display based on this passed parameter - id.
Portfolio.js file
export default class Portfolio extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "Portfolio",
header: null,
};
};
constructor(props) {
super(props);
this.state = {
loading: true,
PortfolioSource: []
};
}
componentDidMount() {
fetch("http://127.0.0.1:8000/portfolios/")
.then(response => response.json())
.then((responseJson) => {
this.setState({
loading: false,
PortfolioSource: responseJson
})
})
.catch(error => console.log(error)) //to catch the errors if any
}
FlatListItemSeparator = () => {
return (
<View style={{
height: .5,
width: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
}}
/>
);
}
renderItem = (data) =>
<TouchableOpacity style={styles.list} onPress={() => this.props.navigation.push('Details', { portid: data.item.id })} >
<Text style={styles.lightText}>{data.item.id}</Text>
<Text style={styles.lightText}>{data.item.portfolio_id}</Text>
<Text style={styles.lightText}>{data.item.name}</Text>
<Text style={styles.lightText}>{data.item.description}</Text>
<Text style={styles.lightText}>{data.item.gains}</Text></TouchableOpacity>
render() {
if (this.state.loading) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9" />
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.PortfolioSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
/>
</View>
)
}
}
PortfolioDetails.js
export default class PortfolioDetails extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "PortfolioDetails",
header: null,
};
};
constructor(props) {
super(props);
this.state = {
loading: true,
PortfolioDetailsdataSource: [],
};
}
componentDidMount() {
fetch(`http://127.0.0.1:8000/stocks/`)
.then(response => response.json())
.then((responseJson) => {
this.setState({
loading: false,
PortfolioDetailsdataSource: responseJson
})
})
.catch(error => console.log(error)) //to catch the errors if any
}
FlatListItemSeparator = () => {
return (
<View style={{
height: .5,
width: "100%",
backgroundColor: "rgba(0,0,0,0.5)",
}}
/>
);
}
goToPrevScreen = () => {
this.props.navigation.goBack();
}
renderItem = (data) =>
<TouchableOpacity style={styles.list}>
<Text style={styles.lightText}>{data.item.id}</Text>
<Text style={styles.lightText}>{data.item.ticker}</Text>
<Text style={styles.lightText}>{data.item.price}</Text>
<Text style={styles.lightText}>{data.item.market_cap}</Text>
<Text style={styles.lightText}>{data.item.YTD}</Text>
<Text style={styles.lightText}>{data.item.OneYear}</Text>
<Text style={styles.lightText}>{data.item.TwoYear}</Text>
<Text style={styles.lightText}>{data.item.TTM_Sales_Growth}</Text>
<Text style={styles.lightText}>{data.item.PE_Ratio}</Text>
</TouchableOpacity>
render() {
if (this.state.loading) {
return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9" />
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={this.state.PortfolioDetailsdataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
/>
<Text> portid: {this.props.navigation.state.params.portid} </Text>
<Button
onPress={() => this.goToPrevScreen()}
title="go back to Portfolio"
/>
</View>
)
}
}
You can use .find(). For example:
PortfolioDetailsDataSource.find(item => item.id === this.props.navigation.state.params.portId)
Assuming IDs are unique, this will return the desired object, otherwise it will return the first occurrence that passes the condition.

How to get ref in flat list item onpress?

I am trying to capture screen with react-native-view-shot. On press this.refs.viewShot.capture showing undefined.
Here is my code
Flat list code:
<FlatList
ref={(list) => this.myFlatList = list}
data={this.state.newsListArray}
keyExtractor={this._keyExtractor}
renderItem={this.renderRowItem}
/>
render on press link:
<TouchableOpacity onPress={ () => {
Platform.OS === 'ios' ?
this._captureScreenIos('5c63f7307518134a2aa288ce') :
this._captureScreenAndroid('5c63f7307518134a2aa288ce')
}}>
<View style={{flexDirection:'row'}}>
<Icon name="share-alt" size={16} color="#ffb6cf" />
<Text style={{paddingLeft:6,fontSize:12,fontWeight:'500'}}>Share News</Text>
</View>
</TouchableOpacity>
And that's the function:
_captureScreenIos = (refId) => {
console.log("Clicked for IOS");
this.changeLoaderStatus();
var thisFun = this;
var viewShotRef = 'viewShot-5c63f7307518134a2aa288ce';
this.myFlatList.viewShot.capture({width: 2048 / PixelRatio.get(), height: 2048 / PixelRatio.get()}).then(res => {
RNFetchBlob.fs.readFile(res, 'base64').then((base64data) => {
console.log("base64data",base64data)
let base64Image = `data:image/jpeg;base64,${base64data}`;
const shareOptions = {
title: "My Beauty Squad",
//message: "Download my beauty squad with below link."+ "\n" + "https://itunes.apple.com/uk/app/my-beauty-squad/id1454212046?mt=8" ,
url: base64Image,
subject: "Share news feed"
};
Share.open(shareOptions);
thisFun.changeLoaderStatus();
})
}).catch(error => {
console.log(error, 'this is error');
this.changeLoaderStatus();
})
}
Please let me know if anyone having a solution for the same.
**This is my app screen **
It's blur when we have long list items.
Try this:
import { captureRef } from react-native-view-shot
constructor(props) {
super(props);
this.refs = {};
}
renderItem = ({item, index}) => (
<TouchableOpacity
onPress={ () => {
captureRef(this.refs[`${index}`], options).then(.....)
}
>
<View
style={{flexDirection:'row'}}
ref={shot => this.refs[`${index}`] = shot}
>
...........
</View>
</TouchableOpacity>
)
React Native View Shot
I hope it help you.
That is a good amount of code. Try https://reactnativecode.com/take-screenshot-of-app-programmatically/
setting the state and try passing in the object you are referencing.
export default class App extends Component {
constructor(){
super();
this.state={
imageURI : 'https://reactnativecode.com/wp-content/uploads/2018/02/motorcycle.jpg'
}
}
captureScreenFunction=()=>{
captureScreen({
format: "jpg",
quality: 0.8
})
.then(
uri => this.setState({ imageURI : uri }),
error => console.error("Oops, Something Went Wrong", error)
);
}
Here is answer:
constructor(props) {
this.screenshot = {};
}
This is my function:
_captureScreenIos(itemId) {
this.changeLoaderStatus();
var thisFun = this;
var viewShotRef = itemId;
captureRef(this.screenshot[itemId],{format: 'jpg',quality: 0.8}).then(res => {
RNFetchBlob.fs.readFile(res, 'base64').then((base64data) => {
console.log("base64data",base64data)
let base64Image = `data:image/jpeg;base64,${base64data}`;
const shareOptions = {
title: "My Beauty Squad",
//message: "Download my beauty squad with below link."+ "\n" + "https://itunes.apple.com/uk/app/my-beauty-squad/id1454212046?mt=8" ,
url: base64Image,
subject: "Share news feed"
};
Share.open(shareOptions);
thisFun.changeLoaderStatus();
})
}).catch(error => {
console.log(error, 'this is error');
this.changeLoaderStatus();
})
}
This is the view:
<View collapsable={false} ref={(shot) => { this.screenshot[itemId] = shot; }} >
//some content here
<TouchableOpacity onPress={ () => {
Platform.OS === 'ios' ?
this._captureScreenIos(itemData.item._id) :
this._captureScreenAndroid(itemData.item._id)
}}>
<View style={{flexDirection:'row'}}>
<Icon name="share-alt" size={16} color="#ffb6cf" />
<Text style={{paddingLeft:6,fontSize:12,fontWeight:'500'}}>Share News</Text>
</View>
</TouchableOpacity>
</View>