Pass data of array to react native view - arrays

I have a function that fetch the data from backend and map over the data and then add them to array
called events_data
function getvals() {
return fetch('http://**********/users/timetable')
.then((response) => response.json())
.then((output) => {
addData(output, events_data);
})
.catch(error => console.log(error))
}
function addData(data, data2) {
data.map((d) => {
data2.push({
title: d.name,
startTime: genTimeBlock(d.day, d.start_time),
endTime: genTimeBlock(d.day, d.end_time),
location: d.location,
extra_descriptions: [d.extra_descriptions],
});
});
}
So in my app view I want to pass events_data to events props:
<SafeAreaView style={{ flex: 1, padding: 30 }}>
<View style={styles.container}>
<TimeTableView
scrollViewRef={this.scrollViewRef}
events={// events.data will be passed here as array format //}**
pivotTime={8}
pivotDate={this.pivotDate}
numberOfDays={this.numOfDays}
onEventPress={}
headerStyle={styles.headerStyle}
formatDateHeader="dddd"
locale="en"
/>
</View>
</SafeAreaView>
Side note: the timetable view it is a third party package that accept array passed in porp events={} and display its data in timetable format
so here I want to pass events_data array coming from function addData and pass it to events prop in <TimeTableView>

function getvals() {
return fetch('http://**********/users/timetable')
.then((response) => response.json())
.then((output) => {
return addData(output, events_data); //<-- add a return statement.
})
.catch(error => console.log(error))
}
in your class component where you are calling the get Val function.
const data = getvals();
this.setState({ events: data });
then you can use this.state.events in your table.

Related

Proper way to push a new item onto a state object's array in react native?

I'm struggling to properly add a new element to my state object's array field:
constructor(props) {
super(props)
this.state = {
mockInterest: '',
interest: [],
testUser: new User()
}
}
addItem = () => {
this.setState({
// this adds interest to local interest array
interest: this.state.interest.concat(this.state.mockInterest)
})
}
handleClick = () => {
console.log("logged interest: " + this.state.mockInterest)
// this logs blank elemnts into the array - no good
this.setState({
testUser: {
interest_tags: [...this.state.testUser.interest_tags, this.state.interest]
}
})
console.log("New interests: " + this.state.testUser.interest_tags)
}
<View style={styles.interestInputView}>
{/* Interest Input */}
<TextInput
style={styles.interestInputText}
placeholder='Enter an Interest'
onChangeText={(text) => {
this.setState({ mockInterest: text })
}}
>
</TextInput>
<TouchableOpacity
style={styles.plusImageContainer}
onPress={this.addItem, this.handleClick}>
<Image
style={styles.plusImage}
source={require('../assets/baseline_add_black_18dp.png')}
/>
</TouchableOpacity>
</View>
Currently, I can only save the text as a mockInterest but when I want to add that to the testUser object (which has an interest_tag array field) in the state, the mockInterest isn't added to the array, interest_tags.
What am I doing wrong? I tried implementing this solution but that didn't work either.
Tyr to use prevState
this.setState(prevState => ({
testUser: {
...prevState.testUser,
interest_tags: [this.state.mockInterest, ...prevState.testUser.interest_tags]
},
}));
Note: setState is async call So you need to wait for a second before reading its value.

React fetching parameters from URL

I'm new to React and have been using it for couple weeks.
I was wondering how to make my cod work to dynamically get data from url and display it
searchSome() {
var serachValue=
"http://localhost:8005/api?act=search&term=" + this.state.valSearch+ "+";
fetch(serachValue)
.then(data => data.json())
.then(result => {
// console.log(this.state.library);
var object= JSON.stringify(this.state.library);
var stringify = JSON.parse(object);
});
}
The link result in data such as id, name etc.
I need to filter that as the user enters words in the search bar and display it.
display code
const filter = this.state.library.filter(book=> {
return (
book.name.indexOf(this.searchSome()) !== -1 ||
book.description
.indexOf(this.searchSome()) !== -1
);
});
Any help much appreciated!
Try below
searchSome(searchTerm) {
var serachValue=
"http://localhost:8005/api?act=search&term=" + searchTerm + "+";
fetch(serachValue)
.then(data => data.json())
.then(result => {
// Not sure what is your format, but you have to set this data to state
this.setState({ library: result });
});
}
render() {
return (
<View>
<TextInput
onChangeText={(text) => {
// Should debounce the call to `searchSome` to avoid multiple request
this.searchSome(text);
this.setState({ searchValue: text })
}}
text={this.state.searchValue}
/>
<FlatList
data={this.state.library}
/>
</View>
)
}

State change detected only when screen is tapped

I have a few TouchOpacity components which fire a function when pressed. This function retrieves data and then sets state.
const Summary = () => {
const [timeSpan, setTimeSpan] = useState('Day');
const [derivedData, setDerivedData] = useState({
chartTimeSpan: 'Day',
sales: [],
totalSales: 0,
orders: 0,
logins: 0,
});
const _fetchSummary = async (timeSpan) => {
console.log(`_fetchSummary HIT : ${timeSpan}`);
try {
const res = await axios.get(`/summary/${timeSpan.toLowerCase()}`);
const { loginCount, orderQty, sales, totalSales } = res.data;
await setDerivedData({
chartTimeSpan: timeSpan,
sales,
totalSales,
orders: orderQty,
logins: loginCount,
});
await setTimeSpan(timeSpan);
console.log(timeSpan, loginCount, orderQty, sales, totalSales);
} catch (err) {
console.log(err);
}
};
const _switchTimeSpan = (newTimeSpan) => {
console.log(`TimeSpan : ${timeSpan}`);
console.log(`NewTimeSpan : ${newTimeSpan}`);
if (timeSpan !== newTimeSpan) {
_fetchSummary(newTimeSpan);
}
};
const { chartTimeSpan, sales, totalSales, orders, logins } = derivedData;
console.log(derivedData);
return (
<>
<TouchableOpacity onPress={() => _switchTimeSpan('Day')}>
<Text style={dropDownItemStyle}>Day</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => _switchTimeSpan('Week')}>
<Text style={dropDownItemStyle}>Week</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => _switchTimeSpan('Month')}>
<Text style={dropDownItemStyle}>Month</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => _switchTimeSpan('Year')}>
<Text style={dropDownItemStyle}>Year</Text>
</TouchableOpacity>
</>
);
};
Everything works fine. The data gets fetched when i click the buttons too. However state doesn't get updated after the data is fetched. I know this because console.log(derivedData); just above the return statement doesn't run. When i tap anywhere on the screen the console.log(derivedData); gives the expected output. Note that i have not set any function which detects this event when i touch the screen.
I have used the derivedData in some other components but did not include those for simplicity sake.
The console.log(derivedData) will be run, when the components need to be rerendered. This depends on the injected state and props variables. Since state variables are not used in JSX, there is no need to rerender the component and log the new derivedData.
You could bypass this by using import { useEffect } from 'react'. Try to log derived data with:
useEffect(() => {
console.log(derivedData);
});
This issue was caused because I have included the following lines of code in App.js file.
XMLHttpRequest = GLOBAL.originalXMLHttpRequest
? GLOBAL.originalXMLHttpRequest
: GLOBAL.XMLHttpRequest;
// fetch logger
global._fetch = fetch;
global.fetch = (uri, options, ...args) => (global._fetch(uri, options, ...args)
.then((response) => {
console.log('Fetch', { request: { uri, options, ...args }, response });
return response;
}));
The above code allows to detect the network requests. I don't know why but although the above code shows the network requests it also delays the data passed from requests to go into the component unless the screen is tapped.
Commenting out the above lines in production lets the code work as it should.

Increment value in react native

I'm getting data from a payload which has a total number of likes on each post. On the user screen, there's an icon for the user to like a post and what i want to achieve is when the user taps on it, the value show be increased to plus 1 against that particular post
VIEW:
{
posts.map((item, i) => {
return (
<View key={i} style={styles.user}>
<Card>
<ListItem
titleStyle={{ color: '#36c', fontWeight:'500' }}
titleNumberOfLines={2}
hideChevron={false}
roundAvatar
title={item.headline}
avatar={{uri:'https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg'}}
/>
<Text style={{marginBottom: 10, fontSize:16, color:'#4a4a4a', fontFamily:'HelveticaNeue-Light'}}>
{item.text}
</Text>
<TouchableOpacity style={styles.likeContainer}>
<Text style={{fontSize:14}}>{item.likesCount}{"\n"}</Text>
<Icon
onPress={()=>onLikePost(item)}
name='md-thumbs-up'
type='ionicon'
iconStyle={[(item.isLiked=== true) ? styles.likedColor : styles.unLikedColor]}
/>
</TouchableOpacity>
</Card>
</View>
);
})
}
CONTAINER:
state = {
posts : [],
id: '',
user: ''
}
componentDidMount = () => {
const { navigation } = this.props;
this.setState({
id : navigation.getParam('id'),
user: navigation.getParam('user')
}, ()=> this.getData())
}
getData = () => {
const api = create({
baseURL: 'https://url.com/api',
headers: {'Accept': 'application/json'}
});
api.get('/groups/'+`${this.state.groupID}`+'/posts').then((response) => {
let data = response.data.data
this.setState({ posts: data });
console.log(JSON.stringify(this.state.posts))
})
}
onLikePost = (item) => {
item.likeCount = item.likeCount+1
}
You are storing posts data in state variable so use setState to update it. Use map and check for each post, whenever id (unique property of each post) matches to id of the clicked item, increase its likesCount otherwise return the same data.
Write it like this:
onLikePost = (item) => {
this.setState(prevState => ({
posts: prevState.posts.map(el => el.id === item.id? {...el, likesCount: el.likesCount+1} : el)
}))
}
Update: Put the check before updating the count value and change the isLiked bool also.
onLikePost = (item) => {
this.setState(prevState => ({
posts: prevState.posts.map(el => {
if(el.id === item.id) {
return {
...el,
isLiked: !el.isLiked,
likesCount: !el.isLiked? el.likesCount+1 : el.likesCount-1,
}
}
return el;
})
}))
}
Note: I am assuming each post has a key id unique value, if it doesn't exist then use any other unique property of the each post.
If array sequence is not an issue, you can use item index and use setState to update it.
<Icon
onPress={()=>onLikePost(i)}
...
/>
...
onLikePost = (i) => {
let posts = this.state.posts;
posts[i].likesCount = !posts[i].isLiked ? posts[i].likesCount + 1 : posts[i].likesCount - 1;
posts[i].isLiked = !posts[i].isLiked;
this.setState({ posts: posts})
}

React native data not rendered after setstate

So i have been working with firebase as a backend in my react native application, i have tried to fetch data this way but i have nothing rendered, i have the activity indicator that went off, but i get that the data array is empty in the application screen, and when i do a console.log, i can see the data in the console, but nothing shows off in the application screen, please help me it's been days that i'm struggling.
export default class Leaderboard extends React.Component{
constructor(props){
super(props)
this.state = {
loading : true,
data : []
}
}
componentDidMount(){
firebase.firestore().collection('rankings').get()
.then(res => {
let rankArray = []
res.forEach(document => {
rankArray.push(document.data())
})
return rankArray;
}).then(res =>{
let data = []
res.forEach(item =>{
firebase.firestore().doc(item.idUser.path)
.get()
.then(doc =>{
let dataItem = {}
dataItem.id = doc.ref.path
dataItem.name = doc.data().fullname
dataItem.points = doc.data().points
dataItem.lc = 'Oran'
data.push(dataItem)
dataItem = {}
})
})
return data;
}).then(res =>this.setState({
loading : false,
data : res
}) ).catch(err => console.log(err))
}
render(){
if(this.state.loading){
return(
<View style = {styles.container}>
<ActivityIndicator size= 'large'></ActivityIndicator>
</View>
)
}else{
console.log(this.state.data)
return(
<View>
<Text>{this.state.data.length}</Text>
<FlatList
data={this.state.data}
renderItem={({item}) => <Text>{item.fullname}</Text>}
/>
</View>
)
}
}
}
The reason for this not working as expected is that you're trying to perform an asynchronous function call, per iteration of your res array inside of your forEach() callback:
// This is asynchronous
firebase.firestore().doc(item.idUser.path).get().then(doc =>{ ... })
Consider revising your code to use the Promise.all() method instead. This will ensure that each asynchronous for individual documents per-item in res array is completed, before setState() in the susequent .then() handler is invoked:
.then(res => {
let rankArray = []
res.forEach(document => {
rankArray.push(document.data())
})
return rankArray;
})
.then(res => {
// Use promise all to resolve each aync request, per item in the
// res array
return Promise.all(res.map(item => {
// Return promise from .get().then(..) for this item of res array.
return firebase.firestore()
.doc(item.idUser.path)
.get()
.then(doc => {
let dataItem = {}
dataItem.id = doc.ref.path
dataItem.name = doc.data().fullname
dataItem.points = doc.data().points
dataItem.lc = 'Oran'
// Return resolve dataItem to array that is relayed to next .then()
// handler (ie where you call this.setState())
return dataItem
})
}));
})
.then(res =>this.setState({
loading : false,
data : res
}))

Resources