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

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

Related

how to get list index using react-native-swiper-flatlist?

the library quotes the getCurrentIndex function, but I can't implement it in my code. Could someone show me an example of use? I couldn't find it anywhere.
You need to pass a ref via the ref prop to the component. This will give you access to the getCurrentIndex function.
Here is a minimal example.
const data = [0, 1, 2, 3, 4]
const App = () => {
const scrollRef = React.useRef(null);
return <View style={styles.container}>
<SwiperFlatList
ref={scrollRef}
data={data}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => console.log(scrollRef.current.getCurrentIndex())} style={{width: 200, height: 200, backgroundColor: "red"}}>
</TouchableOpacity>
)}
/>
</View>
};
The above will print the current index onPress of the rendered item. The key part is scrollRef.current.getCurrentIndex().

Generate touchable oppacity with map using nested array

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>

I want my app to not show articles or give them low priority to those that have already been seen

I have a basic article app like inshorts i am storing all the articles in database which i fetch on the opening and display them in card format.Now I want to implement that when a card id viewed it should get low priority and render at the end next time the app is opened on that mobile.
I have no clue how to implement this.
This is how i am currently rendering it
renderArtciles=()=>{
let len=this.state.dataSource.length;
return this.state.dataSource.map((item,i)=>{
this.state.id=item._id;
this.state.priority=item.priority;
this.state.views=item.views;
if (i == this.state.currentIndex-1)
{
return(
<Animated.View key={item._id} {...this.state.panResponder.panHandlers} style={this.state.swiped_pan.getLayout()}>
< View style={{ flex: 1,position:'absolute',height:height,width:width,backgroundColor:'white'}}>
< View style={styles.Imagebody}>
<Image source={{ uri:item.img.data }} style={styles.image} />
</View>
<View style={styles.inner}>
<Text>{item.body} i==={i}{this.state.currentIndex} </Text>
</View>
</View>
</Animated.View>
)
}
else if (i < this.state.currentIndex)
{
return null
}
if (i == this.state.currentIndex)
{
return(
<Animated.View key={item._id} {...this.state.panResponder.panHandlers} style={this.state.pan.getLayout()}>
< View style={{ flex: 1,position:'absolute',height:height,width:width,backgroundColor:'white'}}>
< View style={styles.Imagebody}>
<Image source={{ uri:item.img.data }} style={styles.image} />
</View>
<View style={styles.inner}>
<Text>{item.body} i==={i}{this.state.currentIndex} </Text>
</View>
</View>
</Animated.View>
)
}
else{
return(
<Animated.View key={item._id} >
< View style={{ flex: 1,position:'absolute',height:height,width:width,backgroundColor:'white'}}>
< View style={styles.Imagebody}>
<Image source={{ uri:item.img.data }} style={styles.image} />
</View>
<View style={styles.inner}>
<Text>{item.body} i==={i}{this.state.currentIndex} </Text>
</View>
</View>
</Animated.View>
)
}
}
).reverse()
}
You could make use of AsyncStorage to store which items have been viewed (and how many times) in a JSON object which you can increment every time an item is viewed, and then retrieve again when the app is opened (and store in some state variable). You can then work out your ordering/priority logic based on the number of views.
To store the items you would do something like this:
_storeData = async () => {
try {
var itemsJson = {items: [
{item_id: 'foo', view_count: 10},
{item_id: 'bar', view_count: 5}
]}
await AsyncStorage.setItem('ItemViews', JSON.stringify(itemsJson));
} catch (error) {
// Error saving data
}
};
And to retrieve the items on app open you would do something like this:
_retrieveData = async () => {
try {
const items = await AsyncStorage.getItem('ItemViews');
// Rest of your code
} catch (error) {
// Error retrieving data
}
};

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>
);
}

React-Native ViewPager goToPage Not Working

import ViewPager from 'react-native-viewpager';
constructor(props){
super(props);
this._renderPage = this._renderPage.bind(this);
this._renderRowSablon = this._renderRowSablon.bind(this);
this.PageChange = this.PageChange.bind(this);
this.count = 0;
this.state = {
count: 0,
info : this.props.values,
page: 0,
pages:pages,
}
}
PageChange(x){
switch(x){
case 'next':
if( this.state.count< (this.state.info.sayfa - 1) ){
this.viewpager.goToPage(this.state.count + 1);
this.setState({count: this.state.count+ 1});
}
break;
}
}
render(){
<View style={{flex:1}}>
<ViewPager
ref={(viewpager) => {this.viewpager = viewpager}}
dataSource={this.state.dataSource}
renderPage={this._renderPage}
onChangePage={this._pageChange}
isLoop={false}
renderPageIndicator={false}
locked={true}
autoPlay={false}/>
</View>
<View style={{flex:1}}>
<TouchableHighlight onPress={() => { this.PageChange('next'); }}>
<Text>Next</Text>
</TouchableHighlight>
</View>
}
When the setState function is cleared, page change is taking place. When the setState function is added (as above) setState works but the page change (gotoPage) does not work. Does not show error / warning What exactly is the problem?
It seems that you are out of scope. When using arrow functions, the scope of 'this' becomes lexical.
Try changing <TouchableHighlight onPress={() => { this.PageChange('next'); }}>
to <TouchableHighlight onPress={this.PageChange('next')}>
See this question:
React-Native: Cannot access state values or any methods declared from renderRow