javascript React Native - javascript

Hi (Sorry for my english)
I have a problem, I consume a service that bring me a buttons array for a dynamic menu. This array I want to put in the screen, like button have a id for a action in specific.
Code
var sizes = []
for (var i = 0; i < this.state.tallas.length; i++) {
sizes.push(
<TouchableOpacity style={[styles.clothingSizeItem, this.state.idSize === this.state.tallas[i].id ? styles.activeClothingSizeItem:null]}
onPress={() => this.fetchSizeData(this.state.tallas[i].id)}>
<Text style={this.state.idSize === this.state.tallas[i].id ? styles.activeSizeText:null}>
{this.state.tallas[i].name}
</Text>
</TouchableOpacity>
);
}
View
render() {
return(
{sizes}
)
}
The problem is when I press a button from my dinamic menu, the button call a function that receive a parameter, but this parameter comes from service array. So I use "i" param from for, but this variable ("i") can't found after that the screen was rendered.
Screen Shot
error screen
Gracias!

You can use Array#map instead of a for loop:
const sizes = this.state.tallas.map(item => (
<TouchableOpacity style={[styles.clothingSizeItem, this.state.idSize === item.id ? styles.activeClothingSizeItem:null]}
onPress={() => this.fetchSizeData(item.id)}>
<Text style={this.state.idSize === item.id ? styles.activeSizeText:null}>
{item.name}
</Text>
</TouchableOpacity>
);
The reason the for loop doesn't work here is that the index variable i is incremented on every iteration of the loop, so by the time the callback ends up being called (long after the loop has completed), the value i is always it's final value (same as tallas.length).

Related

React render quantity of components based on a numeric value

I want to pass in a value to a component and render multiple child components based on that value. e.g. if I pass in count={4} in props, then I want to render 4 <Icon/> components. If I pass in 5, I want to render 5, and so on.
At the moment, all I can think to do is to take the value and turn it into an array (i.e. do a for loop and push a placeholder element to an array with each iteration) and then do a map on that array. But that seems like overkill.
Is there a simple solution to this?
You can do it like this:
...
return(
Array.from({length: props.count}, () => <Icon />)
)
You're probably looking a way to map amount of children to be populated. Check this live example: https://snack.expo.io/#zvona/mapping-child-components
This is the core code from that example:
const Icon = ({index}) => (
<View><Text>{`Icon #${index + 1}`}</Text></View>
);
const Icons = ({count}) => (
Array.from({length: count}).map((_item, index) => <Icon index={index}/>)
)
const App = () => {
return (
<View style={styles.container}>
<Icons count={5} />
</View>
);
}
You can use a simple for loop for this
for(let i = 0; i < count ; i++){
return <Icon />
}
If this doesn't work, you can do the following. It's a bit modern es6 function. Try this..
{[...Array(count)].map((x, i) =>
<Icon key={i} />
)}

How to filter an array by ignoring elements with undefined properties?

I am trying to map image URLs to an avatar component. The data I am using is coming from an Google Books API. The data resource is an array of objects (books). Some objects have missing values for the "thumbnail" field I am targeting.
I want to IGNORE these objects if they do not have a value for "thumbnail" and continue mapping through the array.
I am using React Native and Expo
I have tried various methods mentioned on Stack Overflow, including array.filter() with logic on my data before array.map(). None of the recommended solutions have worked.
The error I recieve is:
TypeError: TypeError: undefined is not an object(evaluating 'url.volumeInfo.imageLink.thumbnail')
It appears I can't even have that path inside of an "if" statement, which is strange to me - because even if that value is undefined, why should that matter?
renderCategory() {
if (this.props.adventure.payload[0]) {
const fetched_Book = this.props.adventure.payload;
const filteredThumbnails = fetched_Book.filter(url =>{
// The console log below shows 4 urls before it fails)
console.log('URL', url.volumeInfo.imageLinks.thumbnail)
if(typeof url.volumeInfo.imageLinks.thumbnail === "undefined"){
return false
}
else return true;
})
const allThumbnails= filteredThumbnails.map(book => book.volumeInfo.imageLinks.thumbnail);
return (
<>
<View style={styles.thumbnailContainer}>
{
allThumbnails.map((l, i) =>
<Avatar
key={i}
size="large"
source={{ uri: l }}
style={styles.thumbNail}
onPress={() => this.onSelectedBook(fetched_Book, i)}
/>
)
}
</View>
</>
)
}
else return ''
}
The expected output is a new, filtered array of objects, that can then be mapped into my Avatar Component.
This error means that some part of path to thumbnail does not exist so you need to check if each part of that path exists:
const filteredThumbnails = fetched_Book
.filter(url => !!url && !!url.volumeInfo && !!url.volumeInfo.imageLinks && typeof url.volumeInfo.imageLinks.thumbnail === 'undefined')

Data recorded from FlatList empty on first TouchableOpacity push

Summary of Problem
Inside of my Flatlist, I want to save the value of the row the user has clicked on in an array
Each row of my FlatList is clickable by using TouchableOpaicty.
What I've tried
I've searched the internet without finding any answer and I've tried saving the data to an array in many different ways
Code
FlatList
<FlatList
data={this.state.friends}
renderItem={({item}) => {
console.log('item', item);
return(
<TouchableOpacity
onPress={() => this.add(item.key)}
>
<ImageBackground
style={styles.friendBubble}
source={require('../../assets/images/friendDisplay.png')}
>
<Text style={styles.friendText}>{item.key}</Text>
</ImageBackground>
</TouchableOpacity>
)
}}
/>
TouchableOpacity onPress function
add = (item) => {
this.setState(
{friendsInComp: [...this.state.friends, item]},
console.log('this.state.friend', this.state.friendsInComp)
)
}
Expected Results
Each time I click a row, the value of that row should be appended to the end of the array.
Actual Results
The first time a row is clicked, no value is added to the array. Each subsequent time the row is clicked, the value from the previous click is added to the end of the array.
The reason why the first time you click the button, your console log print undefined is because setState is asynchronous.. thus it directly print the state without waiting the setState to finish the process, if you want to see the latest state after setState finish, you need to put the console.log inside function..
add = (item) => {
this.setState({
friendsInComp: [...this.state.friends, item ]},
() => { console.log('this.state.friend', this.state.friendsInComp) }
)
}
and just to note when you did the [...this.state.friends, item ], it only append initial state of friends with new item that user click, thus it does not keep adding to track what user click. If you need the state to keep updated to keep track of what user click. you can do something like this.
add = (item) => {
const { friendsInComp } = this.state;
this.setState({
friendsInComp: !friendsInComp ? [...this.state.friends, item ] : [...friendsInComp, item]},
() => { console.log('this.state.friend', this.state.friendsInComp) }
)
}
use !friendsInComp if you dont define friendsInComp as empty array inside constructor. otherwise, use friendsInComp.length == 0
hope this help.

How to use a for loop to display an image depending on a position in an index?

This is inside a react-native project:
I have a progress bar that has 10 positions, and each position as a possibility of three states - incomplete, active, complete. There is an image that corresponds to each of those states. I was thinking I could use a for loop that would look for the index number of the page, which I'll store in an object, and depending on the relationship between the position in the loop and and the index number, that would determine which image gets displayed. Something like this:
if the position in the loop > index number, display the incomplete image
if the position in the loop = index number, display the active image
if the position in the loop < index number, display the complete image
Beyond this, I'm not even sure on where to start.
In your class/component:
state = {
indexNum: 4, // arbitrary value
}
displayStatus(item) {
if(item.id > this.state.indexNum){ // Incomplete
return <View style={styles.progressPoint}><Text>I</Text></View>;
}
else if(item.id == this.state.indexNum){ // Active
return <View style={styles.progressPoint}><Text>A</Text></View>;
}
else if(item.id < this.state.indexNum){ // Complete : you can use only 'else' here
return <View style={styles.progressPoint}><Text>C</Text></View>;
}
}
In render() of your class/component:
// Positions/Pages - these will serve as basis for .map - you can add more than 'id'
const positions = [{"id": 1},{"id": 2},{"id": 3},{"id": 4},{"id": 5},
{"id": 6},{"id": 7},{"id": 8},{"id": 9},{"id": 10}];
return (
<View style={styles.container}>
{positions.map((item) => (
this.displayStatus(item)
))}
</View>
);
Here's an Expo Snack of the above (based on the scope of your question) to get you started.
You can store the index number of the page in state and update this state at completion of each progress position. Note: if you are not using redux, you may need to pass/handle state (index number) on each page individually (depending on your navigation or components structure).

MobX - Pass parameter to action as observable to change observable value?

I have a MobX store, and a react native class setup and working, but not as intended. I have a list of observable user preferences in the UserPreferencesStore like this:
class UserPreferencesStore {
#observable userPreferences = {
receive_upvotes_mail: 0,
receive_answers_mail: 0,
receive_comments_mail: 1
}
}
In the react native class, I want to update the above observables based on onPress of TouchableHighlight like this:
<TouchableHighlight onPress={() => this.onPressPreference('receive_upvotes_mail')}>
<Image source={require('images/Email.png')}
style={UserPreferencesStore.userPreferences.receive_upvotes_mail == 1 && styles.active} />
</TouchableHighlight>
<TouchableHighlight onPress={() => this.onPressPreference('receive_answers_mail')}>
<Image source={require('images/Email.png')}
style={UserPreferencesStore.userPreferences.receive_answers_mail == 1 && styles.active} />
</TouchableHighlight>
<TouchableHighlight onPress={() => this.onPressPreference('receive_comments_mail')}>
<Image source={require('images/Email.png')}
style={UserPreferencesStore.userPreferences.receive_comments_mail == 1 && styles.active} />
</TouchableHighlight>
and here is the onPressPreference action to update the observables that does not work...
#action onPressPreference = (preferenceName) => {
// alert(preferenceName);
UserPreferencesStore.userPreferences.preferenceName = UserPreferencesStore.userPreferences.preferenceName == 0 ? 1 : 0;
}
The preferenceName parameter gets passed perfectly, as the alert shows it, but it does not seem to work when "appended" here UserPreferencesStore.userPreferences.preferenceName to update the passed preference observable store value.
However, If I create 3 actions, one for each preference, and trigger them onPress, then it works and the observable updates the value correctly in the store, but it's a lot of code repeated, and I really want to get it to work with the passed parameter to a single action.
Any idea how to get the passed parameter to work with the single action to update the respective observable's value?
You need to use bracket notation to access a property with variable name.
UserPreferencesStore.userPreferences[preferenceName] = UserPreferencesStore.userPreferences[preferenceName] == 0 ? 1 : 0;

Categories

Resources