accessing props using ref call back - react-native

I am scratching my head hard; trying to figure out what's wrong in below snippet.
import React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
class MyButton extends React.Component {
setNativeProps = (nativeProps) => {
alert(JSON.stringify(this._root.props)) //able to get this.v here
}
render() {
return (
<View ref={cc => {this._root = cc; this.v = 100 }} me="tom">
<Text ref={component => { this._root1 = component;}} style={{margin:55}} onPress={()=>this.setNativeProps({text:'fgfg'})}>{this.props.label} </Text>
</View>
)
}
}
export default class App extends React.Component {
render() {
return (
<TouchableOpacity >
<MyButton label="Press me!" />
</TouchableOpacity>
)
}
}
basically trying to get the props from <View> element i.e. this._root.props using ref callback
though this._root1.props works perfectly fine all the times.
could someone help me figure out what's the problem with it?
EDIT:
I am even able to see this._root but even not this._root.props.me.

Could you try not to do
alert(JSON.stringify(this._root.props))
instead just do
alert(this._root.props)
i.e. remove JSON.stringify
The reason it's not working is because View is having a child element with-in itself while with using Text it's not having any child in it.

Related

Adding a function to the passed this.props.onPress-function in a React Native app

I have changed all the TouchableOpacity-components to a custom component so I can add a universal function to all the buttons / clickable views in my application. I call the new component HapticButton.
All the HapticButton-components will contain onPress properties like this:
<HapticButton activeOpacity={1.0} onPress={() => { console.log("button was pressed"); }}>...</HapticButton>
And my HapticButton-class looks like this:
import React from 'react';
import { TouchableOpacity, Text} from 'react-native';
export default class HapticButton extends React.Component {
render() {
return (
<TouchableOpacity activeOpacity={this.props.activeOpacity} style={[this.props.style]} onPress={this.props.onPress}>
{this.props.children}
</TouchableOpacity>
);
}
vibrate() {
// Code that makes a haptic feedback when called
}
};
I succesfully pass on the onPress-property to my new HapticButton-component, but how do I merge together my the this.props.onPress-property with the vibrate()-function so that this gets called every time the HapticButton is pressed?
You can easily merge two function call together as below
<TouchableOpacity onPress={() => {
this.props.onPress();
this.vibrate();
}}>
or you can directly invoke this.props.onPress() in the vibrate function
<TouchableOpacity onPress={this.vibrate} ...
vibrate() {
this.props.onPress();
// Code that makes a haptic feedback when called
}
Not much differences based on your use case between the two ways I've shared, readability wise I think first way is better

React Native components seem to be sharing a state

I'm having an issue with React-native where I have a component TouchTimer which uses an AnimatedTimer component. This timer is supposed to start and stop when it is tapped, which it does, however all of the TouchTimer components I add to a page will start and stop whenever any of them are tapped, rather than only affecting the tapped component.
Below is a snippet of my component:
TouchTimer.tsx
export class TouchTimer extends React.Component<TouchTimerProps> {
state: {
...
paused: boolean,
}
constructor(props) {
super(props);
...
this.state = {
...
paused: true,
}
}
startStop() {
this.setState({paused: !this.state.paused});
}
render() {
const { time } = this.props;
return (
<TouchableHighlight onPress={() => this.startStop()}>
<View>
<AnimatedTimer
...
time={time}
pause={this.state.paused}
/>
<View style={styles.timeContainer}>
<Text style={styles.time}>{this.state.remaining}</Text>
</View>
</View>
</TouchableHighlight>
)
}
}
And here is a snippet of the screen containing these components:
Details.tsx
import { TouchTimer } from '../components/TouchTimer';
...
export class RecipeDetailsScreen extends React.Component<NavigationInjectedProps> {
...
{this.state.steps.map(step => (
<List.Item
key={step.id}
title={"Step " + step.index}
style={styles.step}
description={step.short_desc}
right={() => (step.time > 0 &&
<TouchTimer
time={step.time * 60000}
/>
)}
/>
)
}
I have tried wrapping the TouchTimer components in a View and changing the paused boolean to a prop, to no avail.
I have also tested to see if this issue appears when the components are not siblings, and when they are not produced as the result of a callback, and the issue still persists in both these cases.
If anybody has any advice or answers on how to make these timers independent I would very much appreciate it!
Curiously that component seems to be implemented with a global pauseFlag that applies to all component instances. See https://github.com/dalisalvador/react-native-animated-timer/blob/master/src/Components/AnimatedTimer.js#L34
So I don't think you're doing anything wrong here, this is a limitation of the library code that is coupling all instances of your timer to the same pauseFlag value.

React-Native this prop randomNumberCount is marked as required in Game ,but its value undefined

Game.js
import React from 'react';
import PropTypes from 'prop-types';
import {View,Text,StyleSheet} from 'react-native';
And this is my code and this is the warning this prop randomNumberCount is marked as required in Game ,but its value undefined. How to solve this warning?
class Game extends React.Component {
static propTypes = {
randomNumberCount: PropTypes.number.isRequired,
};
randomNumbers=Array
.from({length:this.props.randomNumberCount})
.map(() =>1+Math.floor(10*Math.random()));
target=this.randomNumbers
.slice(0,this.props.randomNumberCount-2)
.reduce((acc,curr)=>acc+curr,0);
render() {
return (
<View style={styles.container}>
<Text style={styles.target}>{this.target}</Text>
{this.randomNumbers.map((randomNumber,index)=>
<Text key={index}>{randomNumber}</Text>
)}
</View>
);
}
}
This warning is because you have marked randomNumberCount as required in prototype. So when you use this component, you need to compulsory pass the prop randomNumberCount. Or you can remove the .isRequired from prototype declaration.
You can declare default value for randomNumberCount as
static defaultProps = {
randomNumberCount: dafaultValue
};
For reference:
Native Component

How to correctly large state updates in React Native?

I am writing a small ReactNative application that allows users to invite people to events.
The design includes a list of invitees, each of which is accompanied by a checkbox used to invite/uninvite said invitee. Another checkbox at the top of the list that performs a mass invite/uninvite on all invitees simultaneously. Finally a button will eventually be used to send out the invites.
Because the state of each of these elements depends changes made by the other I often need to re-render my entire UI whenever the user takes action on one of them. But while this works correctly it is causing me quite a few performance issues, as shown in this video
Here's the code I'm using:
import React, { Component } from 'react';
import { Container, Header, Title,
Content, Footer, FooterTab,
Button, Left, Right,
Center, Body, Text, Spinner, Toast, Root , CheckBox, ListItem, Thumbnail} from 'native-base';
import { FlatList, View } from 'react-native';
export default class EventInviteComponent extends Component {
constructor(props) {
super(props);
console.disableYellowBox = true;
this.state = {
eventName: "Cool Outing!",
invitees:[]
}
for(i = 0; i < 50; i++){
this.state.invitees[i] = {
name: "Peter the " + i + "th",
isSelected: false,
thumbnailUrl: 'https://is1-ssl.mzstatic.com/image/thumb/Purple111/v4/62/08/7e/62087ed8-5016-3ed0-ca33-50d33a5d8497/source/512x512bb.jpg'
}
}
this.toggelSelectAll = this.toggelSelectAll.bind(this)
}
toggelSelectAll(){
let invitees = [...this.state.invitees].slice();
let shouldInviteAll = invitees.filter(invitee => !invitee.isSelected).length != 0
let newState = this.state;
newState = invitees.map(function(invitee){
invitee.isSelected = shouldInviteAll;
return invitee;
});
this.setState(newState);
}
render() {
let invitees = [...this.state.invitees];
return (
<Root>
<Container>
<Content>
<Text>{this.state.eventName}</Text>
<View style={{flexDirection: 'row', height: 50, marginLeft:10, marginTop:20}}>
<CheckBox
checked={this.state.invitees.filter(invitee => !invitee.isSelected).length == 0}
onPress={this.toggelSelectAll}/>
<Text style={{marginLeft:30 }}>Select/deselect all</Text>
</View>
<FlatList
keyExtractor={(invitee, index) => invitee.name}
data={invitees}
renderItem={(item)=>
<ListItem avatar style={{paddingTop: 20}}>
<Left>
<Thumbnail source={{ uri: item.item.thumbnailUrl}} />
</Left>
<Body>
<Text>{item.item.name}</Text>
<Text note> </Text>
</Body>
<Right>
<CheckBox
checked={item.item.isSelected}/>
</Right>
</ListItem>}/>
</Content>
<Footer>
<FooterTab>
<Button full
active={invitees.filter(invitee => invitee.isSelected).length > 0}>
<Text>Invite!</Text>
</Button>
</FooterTab>
</Footer>
</Container>
</Root>);
}
}
In your code, in class method toggelSelectAll() {...} you modify the state directly by using this.state = ..., which is something to be avoided. Only use this.state = ... in your class constructor() {...} to initialize the state, and you should only use this.setState({...}) to update the state anywhere else.
Not sure if this should help your performance issues, but try replacing toggelSelectAll() with the following:
toggelSelectAll() {
const {invitees} = this.state;
const areAllSelectedAlready = invitees.filter(({isSelected}) => !isSelected).length === 0;
this.setState({
invitees: invitees.map(invitee => ({
...invitee,
isSelected: !areAllSelectedAlready
}))
});
}
Good luck! And, let me know if you would like me to refactor your above code to remove the 2nd this.state = ... in your constructor (which, once again, should be avoided when writing React).
I suggest:
Dividing your code by creating multiple components, so you won't have a massive render()
Using Redux to store invitee / global state, so you can choose which components should re-render in case of modifications
That's a good way to learn React Native!

Listview blanks when using navigator pop

I am facing two problems & can not resolve this. Can anyone explain why this is happening? Or can anyone give me the solution?
Problem NO 1:
There are two components 1) Home 2) Inner
In the Home there are list of data & when I click any one of them it route to Inner. There are also a back button with using navigator pop. There is the issue sometime reproduce - When I click on back, it routes to Home but there is no data(listview element) but when I touch on the screen the data shows properly.
Problem NO 2:
In the home component within the list data, there also some repeated data within the single row.
I have used map function for this. Sometimes ( Most of the time it shows correctly ) it is also not shown in list view, the row is showing properly but the repeated data are missing.
<ListView onEndReached={this.props.reloadArticles} onEndReachedThreshold={10} dataSource={this.props.dataSource} renderRow={this.renderPost} enableEmptySections={true} refreshControl={ <RefreshControl refreshing={this.props.isRefreshing} onRefresh={this.props._onRefresh} /> } />
NO 1:
Sounds like this: https://github.com/facebook/react-native/issues/8607
If you use rn 0.4.0 setting initialListSize={0} could help
NO 2:
Could you please post your renderRow / dataSource code
#SnowMax Sorry for late reply & thanks for help.
Due to confedential project I could not share all code but I can share u the code structure:
renderPost = (list, sectionID, rowID) => {
if(list && list.is_shared_post == true){
return (
<WallShared list={list} rowID={rowID} />
);
}
}
class WallShared extends Component {
constructor(props) {
super(props);
}
render() {
if( this.props.list.is_shared_post == true){
switch(this.props.list.TblPostsComments.post_type){
case 'T':
return (<ShareText list={this.props.list} />);
default:
return (<ShareDefault list={this.props.list} rowID={this.props.rowID} />);
break;
}
} else {
return (
<View>something</View>
)
}
}
}
export default WallShared;
class ShareText extends Component {
constructor(props) {
super(props);
this.state ={
isLoggedIn:false,
}
}
componentDidMount(){
}
render() {
var ShareBlock = this.props.list.TblPostsComments.shared_contents.map(function(cont, ind) {
return (
<View style={[styles.feedItem, styles.SfeedItem]} key={ind}> </View>
);
},this);
return(
<View style={styles.feedItem}>
<View style={styles.sharedWrap}>
{ ShareBlock }
</View>
</View>
);
}
}
export default ShareText;