Generate touchable oppacity with map using nested array - react-native

I write a calculator app and i need to generate touchable opacity using map
I have a nested array
state= {
buttons:['+', '-', '*', '/', 'Del']
}
And I generate touchable opacity using map
<View style={styles.container}>
<View style={styles.second_con}>
{
this.state.buttons.map((item,index)=>{
return(
<TouchableOpacity style={styles.buttons} key={index}>
<Text>{item}</Text>
</TouchableOpacity>
)
}
)
}
</View>
</View>
And it works
How can i generate it using nested array ?
buttons1:[['√', ' ', 'x!', '+/-', '%'],
['e^x', '10^x', 1, 2, 3],
['ln', 'log', 4, 5, 6],
['e', '^2', 7, 8, 9],
['π', '^3', ',', 0, '='],
]

If you want to preserve the rows you have laid out in your nested array, map through the rows, and use your existing map for each row. Here's an example:
<View style={styles.container}>
<View style={styles.second_con}>
{
this.state.buttons1.map(row => (
<View style={style.row_con}>
{
row.map((item, index) => (
<TouchableOpacity style={styles.buttons} key={index}>
<Text>{item}</Text>
</TouchableOpacity>
))
}
</View>
))
}
</View>
</View>

You need to flatten the list. You can do this with the function below
const flattenLst = (lst)=>{
let res = []
lst.forEach(curSet=>{
res = [...res,...curSet] // TODO: read into spread operators in javascript
})
return res;
}
Then do
<View style={styles.container}>
<View style={styles.second_con}>
{
// flatten this.state.buttons
flattenLst(this.state.buttons).map((item,index)=>{
return(
<TouchableOpacity style={styles.buttons} key={index}>
<Text>{item}</Text>
</TouchableOpacity>
)
}
)
}
</View>
</View>

Related

how to disable some touchableopacity in react native

I display in numbers scrolling app in TouchableOpacity and I want some of the buttons to be disabled and some not how do I do that?
<ScrollView contentContainerStyle={styles.contentContainerStyle}>
{hour.map((item, index) => {
return (
<TouchableOpacity
// delayPressIn={}
disabled={
isDisable
}
style={styles.item}
key={index}
onPress={() => {
setShow(true);
}}>
<Text style={styles.text}> {item}</Text>
</TouchableOpacity>
);
})}
</ScrollView>
Assuming that the hours array is like this [1, 2, 3, 4, 5, 6, 7, ...]
You can include an array of numbers you want to disable in the disabled condition
in this example TouchableOpacity will be disabled for numbers 1, 3, 5, 7 because it will check for the condition when the item is 1 then it will check if 1 is in that array if it is then the condition will be true and it will be like disabled={true} and if the item is 2 then condition will be false because 2 is not in that array and it will be like disabled={false}
your disabled condition will look something like this so you have to list the numbers you want to disable TouchableOpacity for will be in this array
disabled={[1,3,5,7].includes(item)} // TouchableOpacity will be disabled for numbers 1, 3, 5, 7
Example:
<ScrollView contentContainerStyle={styles.contentContainerStyle}>
{hour.map((item, index) => {
return (
<TouchableOpacity
// delayPressIn={}
disabled={[1,3,5,7].includes(item)}
// TouchableOpacity will be disabled for numbers 1, 3, 5, 7
style={styles.item}
key={index}
onPress={() => {
setShow(true);
}}>
<Text style={styles.text}> {item}</Text>
</TouchableOpacity>
);
})}
</ScrollView>

Fetching nested data, undefined is not an object

I'm trying to fetch data from API, but I'm only able to fetch the highest level ones. When I'm trying to access ones nested under categories, I get an error: undefined is not an object (evaluating 'this.state.data.order.name' ).
From what I've read it might be an issue with state but I'm new to react-native and I am not sure how to fix it.
This is the API structure
render(){
const { data } = this.state;
return(
<ScrollView style={styles.containerxd}>
<TouchableOpacity style={styles.textStyle}>
<Image
source={require('./images/burger.png')}
style={styles.ImageIconStyle} />
</TouchableOpacity>
<View style={styles.white}>
<View style={{flex:1, alignItems:'center', justifyContent:'center'}}>
<View style={styles.tabHeader}><Text style={styles.textHeader}>Scientific name</Text></View>
<View style={styles.tabContent}><Text style={styles.textContent}>{this.state.data.scientific_name}</Text></View>
<View style={styles.tabHeader}><Text style={styles.textHeader}>Common name</Text></View>
<View style={styles.tabContent}><Text style={styles.textContent}>{this.state.data.common_name}</Text></View>
<View style={styles.tabHeader}><Text style={styles.textHeader}>Moisture use</Text></View>
<View style={styles.tabContent}><Text style={styles.textContent}>{this.state.data.order.name}</Text></View>
Scientific name and common name show up just fine, but every data level lower renders error.
You need to validate your data.When order is undefined, doing order.name will break your app. change
<View style={styles.tabContent}><Text style={styles.textContent}>{this.state.data.order.name}</Text></View>
to
const { data } = this.state;
const name = data && data.order && data.order.name || '';
// rest of the code here
<View style={styles.tabContent}><Text style={styles.textContent}>{name}</Text></View>
NOTE
Always validate your data. Don't assume that you'll always get the right data. When working with objects always validate them, as doing data.name, can break your app, if data is null or undefined. for example, given the following object.
const animal = {};
doing
// throws an error, Cannot read property 'toLowerCase' of undefined
console.log(animal.name.toLowerCase())
to prevent that from happening, we need to check if the propery exists, like the following.
// checks if the name property exists console name, else assign a console log 'Lion'
console.log(animal.name && animal.name.toLowerCase() || 'Lion')
Second option
add a loader, display Loading... text when fetching data from api, once the request finish set loader to false and display your data.
fetchData = async () => {
const res = await fetch(...)
...
this.setState({ isLoading: false, data: response.data });
}
render() {
return (
<ScrollView style={styles.containerxd}>
<TouchableOpacity style={styles.textStyle}>
<Image
source={require('./images/burger.png')}
style={styles.ImageIconStyle}
/>
</TouchableOpacity>
{this.state.isLoading ? (
<Text>Loading...</Text>
) : (
<View style={styles.white}>
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}}
>
<View style={styles.tabHeader}>
<Text style={styles.textHeader}>Scientific name</Text>
</View>
<View style={styles.tabContent}>
<Text style={styles.textContent}>
{this.state.data.scientific_name}
</Text>
</View>
<View style={styles.tabHeader}>
<Text style={styles.textHeader}>Common name</Text>
</View>
<View style={styles.tabContent}>
<Text style={styles.textContent}>
{this.state.data.common_name}
</Text>
</View>
<View style={styles.tabHeader}>
<Text style={styles.textHeader}>Moisture use</Text>
</View>
<View style={styles.tabContent}>
<Text style={styles.textContent}>
{this.state.data.order.name}
</Text>
</View>
</View>
</View>
)}
</ScrollView>
);
}

React-Native- Cannot Read Property '0' of Undefined

I'm new to react-native. I am building a calculator app through a tutorial where I'm at the point right now where I'm trying to set it up where pressing one of the calculator buttons will log to the console that digit or symbol.
When I press one of the buttons on the calculator, I receive an error in the console 'Cannot read property '0' of undefined' if I click the 1,4,7 buttons but does 1 and 2 as well for 2,5,8 and 3,6,9 buttons respectively. I figure this means that this.buttonPressed isn't able to process my horizontal array for whatever reason, or that there is some other problem.
I've tried binding buttonPressed in the constructor with
this.buttonPressed = this.buttonPressed.bind(this)
to no avail.
export default class App extends Component {
constructor(){
super()
this.state = {}
}
buttonPressed(text) {
console.log(text)
}
render() {
let rows = []
let nums = [[1, 2, 3], [4, 5, 6], [7, 8, 9,], ['.', 0, '=']]
for(i=0; i<4; i++){
let row = []
for(let j=0; j<3; j++){
row.push(
<TouchableOpacity onPress={() => this.buttonPressed(nums[i]
[j])} style={styles.btn}>
<Text style={styles.btnText}>{nums[i][j]}</Text>
</TouchableOpacity>
)
}
rows.push(<View style={styles.row}>{row}</View>)
}
...
return (
<View style={styles.container}>
...
<View style={styles.buttons}>
<View style={styles.numbers}>
{rows}
</View>
</View>
</View>
);
}
}
Use ES6 format.
change:
buttonPressed(text) {
console.log(text)
}
to:
buttonPressed = (text) => {
console.log(text)
}
A better way to approach this problem would be to remove the logic from the render function. This would help when debugging the problem.
The rows/numbers array can be taken outside of the component as it will not change, so no need to recreate it each time the component render function is called. You can then use the map function on the array to render each row and number item. This is the preferred approach when working with react.
You can try the code below, and see if it works. Although there is some code missing from your snippet.
const ROWS = [[1, 2, 3], [4, 5, 6], [7, 8, 9,], ['.', 0, '=']]
export default class App extends Component {
buttonPressed(text) {
console.log(text)
}
render() {
return (
<View style={styles.container}>
<View style={styles.buttons}>
<View style={styles.numbers}>
{ROWS.map(numbers => (
<View style={styles.row}>
{numbers.map(number => (
<TouchableOpacity style={styles.btn} onPress={() => this.buttonPressed(number)}>
<Text style={styles.btnText}>{number}</Text>
</TouchableOpacity>
))}
</View>
))}
</View>
</View>
</View>
);
}
}
An even better approach would be to use a pure component. Then if you need state, you can use react hooks.
const ROWS = [[1, 2, 3], [4, 5, 6], [7, 8, 9,], ['.', 0, '=']]
const App = () => {
const buttonPressed = (text) => {
console.log(text)
}
return (
<View style={styles.container}>
<View style={styles.buttons}>
<View style={styles.numbers}>
{ROWS.map(numbers => (
<View style={styles.row}>
{numbers.map(number => (
<TouchableOpacity style={styles.btn} onPress={() => buttonPressed(number)}>
<Text style={styles.btnText}>{number}</Text>
</TouchableOpacity>
))}
</View>
))}
</View>
</View>
</View>
);
}
export default App

React Native flatlist conditional rendering

I have the following flat list in react native with the following
items.
key
name
type
Now i also have the following renderItem function that is used to render
the elements of the flatlist.
renderItem={({ item }) => (
<View>
<View style={styles.navBarLeftButton}>
<Avatar
medium
rounded
source={{uri:item.name}}
activeOpacity={0.7}
onPress={() => console.log(this.state.data)}
/>
<Text style={styles.textbutton}>{item.type}</Text>
<Text>{item.description}</Text>
<Text>{item.request} <Emoji name={item.request} style={{fontSize: 15}} />
<Emoji name="pray" style={{fontSize: 15}} /></Text>
</View>
</View>
)}
I want to render a different render function base on the item key of the flatlist
Is there away i can do conditional rendering with react native flatlist base
on key?
The renderItem prop for Flatlist can accept 2 arguments, the second being index, so you can do something like
renderItem={({ item, index })=>{
if(index = 0){
//do something
}
}}
Then just throw in a switch or some if statements and you can render conditionally.
Based on 'theme' values in variable DATA (as keys) FlatList renderItem prop is conditionally accepting different Views/Components returned by separate functions
<FlatList
data={DATA}
renderItem={({ item, index }) => {
if (item.theme === 1) {
return this.renderTheme1({ item });
}
return this.renderTheme2({ item });
}}
keyExtractor={item => item.id}
/>
This works for me:
renderItem={({ item }) => (
item.isDeleted == false ?
<View>
<Activity isRunning={item.isRunning} />
</View>
: null
)}
keyExtractor={(item) => item.title}
/>
Are you looking to return two different template sets? Something like:
renderItem={({item, index}) => {
if (index == 0) {
return <View style={{flex: 1, flexDirection: 'row'}}>
<Image source={item.image} style={styles.flatList_imageView} resizeMode="contain"/>
</View>
}
return <View style={{flex: 1, flexDirection: 'row'}}>
<Text>{item.key}</Text>
</View>
}
I think it will help you
renderItem={({ item, index })=>
<View>
{index == 0 ? <Text>Some Text</Text> : <Text>Some Text</Text> }
</View>
}}

Detect touch position in array of view (REACT NATIVE)

I am creating a planning application in react native and i couldn't handle the creation of a new view above a list of rendered views
I want to detect a user touch in map of views and show a view in the touch position, I tried to use
<TouchableOpacity onPress={(evt)=>{this.setTopNewViewPostion(evt.nativeEvent.locationY}}
but this was rendering the view for all slots that mean it is duplicated every time
Thanks
handleClick (){ this.setState({newSlotVisible: true});} renderSlotsOfTab(tab, i){
const ViewFlex = [1, 2, 3, 4];
return (
<TouchableOpacity
onStartShouldSetResponder={() => true}
onMoveShouldSetResponder={() => true}
onResponderMove={()=>this.hideNewSlot()}
ref={(ref) => i}
tabLabel={tab}
key={i}
activeOpacity={1}
>
{
ViewFlex.map((b , i)=>
{
return(
<TouchableOpacity
ref={i}
removeClippedSubviews
style={[styles.oneSlotView ,
{ flex: b }]}
key={i}
/** here get the touch position**/
onPress={this.handleClick.bind(this, i)}
>
<View
style={ styles.hoursIndicatorView}
>
<Text style={styles.textIndicatorTime}>00h00</Text>
</View>
</TouchableOpacity>)
}
)}
{this.state.newSlotVisible?
//the new view to render is this one
this.renderNewView();
:null}
</TouchableOpacity>
);
}