My app is always loading, When i drop a debugger around and add a watch onto variable list & res , i get not available.
I'm not exactly sure what's the problem or am i even debugging it correctly?
please advice. I'm trying to achieve Loaded by loading the json into the list.
Update:
I just did a console log and saw data
console.log(this.state.list);
var facemashTab = React.createClass({
getInitialState: function() {
return {
list: [],
currentIndex: 0
};
},
componentWillMount: function() {
fetch('https://randomuser.me/api/?results=5')
.then(res => res.json())
.then(res => this.setState({ list: res }));
},
render: function() {
var contents;
if (!this.state.list.length) {
contents = (
<View style={ styles.loading }>
<Text style={ styles.loadingText }>Loading</Text>
<ActivityIndicatorIOS />
</View>
)
} else {
contents = (
<View style={ styles.content }>
<Text>Loaded</Text>
</View>
)
}
return (
<View style={ styles.container }>
<View style={ styles.header }>
<Text style={ styles.headerText }>XXX</Text>
</View>
<View style={ styles.content }>
{ contents }
</View>
</View>
);
}
});
Not sure if this is the problem, but from my understanding setState inside componentWillMount will not trigger a render phase ? could you try componentDidMount
Related
So I've seen many posting the same problem, but for some I don't seem to be able to adapt the posted solutions to my case.. I hope someone can tell me exactly what changes I need to do in order to get this working, since I don't know how to implement the suggested solutions!
I am using React Native Swipeable
Example of someone having the same issue
I have a file in which I built the Swipeable Component and an other class which calls the component. I've set a timeout close function on the onSwipeableOpen as a temporary solution. But ideally it should close immediately upon pressing "delete". The "..." stands for other code which I deleted since it's not important for this case.
AgendaCard.js
...
const RightActions = ({ onPress }) => {
return (
<View style={styles.rightAction}>
<TouchableWithoutFeedback onPress={onPress}>
<View style={{ flexDirection: "row", alignSelf: "flex-end" }}>
<Text style={styles.actionText}>Löschen</Text>
<View style={{ margin: 5 }} />
<MaterialIcons name="delete" size={30} color="white" />
</View>
</TouchableWithoutFeedback>
</View>
);
};
...
export class AgendaCardEntry extends React.Component {
updateRef = (ref) => {
this._swipeableRow = ref;
};
close = () => {
setTimeout(() => {
this._swipeableRow.close();
}, 2000);
};
render() {
return (
<Swipeable
ref={this.updateRef}
renderRightActions={() => (
<RightActions onPress={this.props.onRightPress} />
)}
onSwipeableOpen={this.close}
overshootRight={false}
>
<TouchableWithoutFeedback onPress={this.props.onPress}>
<View style={styles.entryContainer}>
<Text style={styles.entryTitle}>{this.props.item.info}</Text>
<Text style={styles.entryTime}>
eingetragen um {this.props.item.time} Uhr
</Text>
</View>
</TouchableWithoutFeedback>
</Swipeable>
);
}
}
Agenda.js
...
renderItem(item) {
...
<AgendaCardAppointment
item={item}
onRightPress={() => firebaseDeleteItem(item)}
/>
...
}
I'm having the same issue and have been for days. I was able to hack through it, but it left me with an animation I don't like, but this is what I did anyways.
export class AgendaCardEntry extends React.Component {
let swipeableRef = null; // NEW CODE
updateRef = (ref) => {
this._swipeableRow = ref;
};
close = () => {
setTimeout(() => {
this._swipeableRow.close();
}, 2000);
};
onRightPress = (ref, item) => { // NEW CODE
ref.close()
// Delete item logic
}
render() {
return (
<Swipeable
ref={(swipe) => swipeableRef = swipe} // NEW CODE
renderRightActions={() => (
<RightActions onPress={() => this.onRightPress(swipeableRef)} /> // NEW CODE
)}
onSwipeableOpen={this.close}
overshootRight={false}
>
<TouchableWithoutFeedback onPress={this.props.onPress}>
<View style={styles.entryContainer}>
<Text style={styles.entryTitle}>{this.props.item.info}</Text>
<Text style={styles.entryTime}>
eingetragen um {this.props.item.time} Uhr
</Text>
</View>
</TouchableWithoutFeedback>
</Swipeable>
);
}
}
I have Header component which I would like to use in multiple screens with multiple use cases such as in MainScreen I want to show only profile icon whereas in other screens I would like to use both backButton and profile icon.
I get isProfileIconVisible and isBackButtonIconVisible from props in Header Component.
this.state = {
isProfileIconVisible: props.isProfileIconVisible,
isBackButtonIconVisible: props.isBackButtonIconVisible
}
I have rendering functions.
_renderProfileIcon () {
let profileIcon = (
<View style={styles.profileButtonContainer} >
<CustomIconButton
onPress={this.props.onProfilePress}
></CustomIconButton>
</View>
);
return profileIcon;
};
_renderBackButtonIcon () {
let backButonIcon = (
<View style={styles.backButtonContainer} >
<CustomIconButton
onPress={this.props.onBackPress}
iconName={"arrow-left"}
></CustomIconButton>
</View>
);
return backButonIcon;
};
and in main render function I am making conditional rendering:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{this.props.text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon : null}
</View>
)
}
with this setup, I am not able to render either ProfileIcon nor BackButtonIcon.
I got the text prop but not icons.
Header Component propTypes and defaultProps:
Header.propTypes = {
onBackPress: PropTypes.func,
onProfilePress: PropTypes.func,
text: PropTypes.string,
backButtonIconName: PropTypes.string,
isProfileIconVisible: PropTypes.bool,
isBackButtonIconVisible: PropTypes.bool,
};
Header.defaultProps = {
backButtonIconName: 'keyboard-backspace',
isProfileIconVisible: true,
isBackButtonIconVisible: true,
}
And this is how I call Header component from another component:
<Header
text={"Welcome!"}
isProfileIconVisible={true}
isBackButtonIconVisible={false}
onProfilePress={this.handleProfileButtonPress}
style={styles.headerContainer}
/>
Can you help me where I am doing wrong?
Thank you.
Your _renderBackButtonIcon and _renderProfileIcon are functions, you need to call them to get their return values:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon() : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{this.props.text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon() : null}
</View>
)
}
Note the () after this._renderBackButtonIcon and this._renderProfileIcon.
Side note: There's no reason to have ...otherProps here:
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
You never use it.
There is an argument for adding text to that list and using it, rather than this.props.text within the return value:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, text } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon() : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon() : null}
</View>
)
}
I have a flat list, which gets its data source as a state. Actually, this data is from firebase, and i have been using redux. So, the data is fetched in the actions, and using callback i get the data to state.
What i want to achieve is, when there is no data found from the api, An empty list message should be show in the view. Actually , i achieved this using "ListEmptyComponent". But whats happening is the screen starts with empty message, and the spinner loads below it, and then if data found the message goes away as well as spinner.
But, what i wanted is, when the view gets rendered the first thing everyone should see is the spinner, and then if data empty spinner hides then empty list message displays.
How to achieve this ?
My Action :
export const fetchOrderHistory = (phone, callback) => {
return (dispatch) => {
dispatch({ type: START_SPINNER_ACTION_FOR_ORDER_HISTORY })
firebase.database().ref('orders/'+phone)
.on('value', snapshot => {
const snapShotValue = snapshot.val();
callback(snapShotValue);
dispatch ({ type: ORDER_HISTORY_FETCHED , payload: snapshot.val()});
dispatch({ type: STOP_SPINNER_ACTION_FRO_ORDER_HISTORY })
});
};
};
My Flat List & spinner:
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card
containerStyle={{ borderRadius: 5 }}
>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }
>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</View>
</Card>
)}
/>
{this.props.isSpinnerLoading &&
<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> }
My Call back at componentWillMount which set state:
componentWillMount() {
this.props.fetchOrderHistory((this.props.phone), (snapShotValue)=> {
const userOrderHistory = _.map(snapShotValue, (val,uid) => ({uid, ...val}))
this.setState({ historyOfOrders: userOrderHistory })
});
}
My EmptyList Message:
onListEmpty = () => {
return <View style={{ alignSelf: 'center' }}>
<Text style={{ fontWeight: 'bold', fontSize: 25 }}>No Data</Text>
</View>
}
My State:
state = { historyOfOrders: "" }
I am getting the spinner values from the reducers, using mapStateToProps.
Kindly Guide me, through
you have to do two things for that.
First, show Flatlist only if the loader is stopped. Second, set default value of this.state.historyOfOrders is null and check if this.state.historyOfOrders not null then only show Flatlist.
Here is a code:
{(!this.props.isSpinnerLoading && this.state.historyOfOrders != null) ?
(
<FlatList
data={this.state.historyOfOrders}
keyExtractor={item => item.uid}
ListEmptyComponent={this.onListEmpty()}
renderItem={({ item }) => (
<Card containerStyle={{ borderRadius: 5 }}>
<View style={styles.topContainerStyle}>
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('ViewOrderScreen', {itemsOfOrder: item}) }>
<View style={styles.viewOrderContainer}>
<View style={styles.viewOrderTextContainer}>
<Text style={styles.viewOrderTextStyle}>View Order</Text>
</View>
<Icon
name='ios-arrow-forward'
type='ionicon'
color='#ff7675'
/>
</View>
</TouchableOpacity>
</View>
</View>
</Card>
)}
/>
) : null
}
With this condition, even if you want loader above Flatlist you can do that.
The path you should take is rendering only the spinner when the loading flag is set and rendering the list when loading flag is false.
Your render method should be like below
render()
{
if(this.props.isSpinnerLoading)
{
return (<View style={styles.loading}>
<ActivityIndicator size="large" color="#03A9F4"/>
</View> );
}
return (/** Actual List code here **/);
}
I'm mapping an array to be rendered in React Native. On an event (button press) I want to add and object to the array and it be rendered on the screen. I am getting the lifecycle functions to trigger, but not sure if they are needed for this or how to utilize them effectively. Any help would be greatly appreciated!
class Home extends Component {
constructor(props) {
super(props)
this.state = {
data: '',
text: '',
submitted: false,
count: 1,
arr: [
{
key: 1,
text: "text1"
},
],
}
buttonsListArr = this.state.arr.map(info => {
return (
<View style={styles.container}>
<Text key={info.key}>{info.text}</Text>
<Button title='Touch' onPress={() => {
this.setState({count: this.state.count + 1})
}}/>
</View> )
})
}
shouldComponentUpdate = () => {
return true;
}
componentWillUpdate = () => {
this.state.arr.push({key: this.state.count, text: 'text2'})
}
render() {
return (
<View style={styles.container}>
{buttonsListArr}
</View>
)
}
}
What you've written is not typical. A quick fix is to move the logic to a render function like
constructor(props) {
.
.
this.renderText = this.renderText.bind(this)
}
renderText() {
return this.state.arr.map(info => {
return (
<View style={styles.container}>
<Text key={info.key}>{info.text}</Text>
<Button title='Touch' onPress={() => {
this.setState({count: this.state.count + 1})
}}/>
</View> )
})
}
then call the function within the render()
render() {
return (
<View style={styles.container}>
{this.renderText()}
</View>
)
}
You shouldn't be using a lifecycle call to add an element to an array. Simply call setState and it will rerender itself!
Try this.
<View style={styles.container}>
<Text key={info.key}>{info.text}</Text>
<Button title='Touch' onPress={() => {
this.setState({
count: this.state.count + 1
arr: this.state.arr.push({key: this.state.count, text: 'text2'})
})
}}/>
</View> )
return this.arrayholder.map(image => {
return (
<View style={styles.ImageContainer}>
<Image style={styles.ImageContainer} source={image} />
</View>
)
})
How do I bind a function outside of scope in React Native? I'm getting the errors:
undefined is not an object evaluating this.state
&
undefined is not an object evaluating this.props
I'm using the render method to evoke renderGPSDataFromServer() when the data has been loaded. The problem is, I'm trying to use _buttonPress() and calcRow() inside of renderGPSDataFromServer(), but I'm getting those errors.
I've added
constructor(props) {
super(props);
this._buttonPress = this._buttonPress.bind(this);
this.calcRow = this.calcRow.bind(this);
to my constructor and I've changed _buttonPress() { to _buttonPress = () => { and still nothing.
I think I understand the problem but I don't know how to fix it:
renderLoadingView() {
return (
<View style={[styles.cardContainer, styles.loading]}>
<Text style={styles.restData}>
Loading ...
</Text>
</View>
)
}
_buttonPress = () => {
this.props.navigator.push({
id: 'Main'
})
}
renderGPSDataFromServer =() => {
const {loaded} = this.state;
const {state} = this.state;
return this.state.dataArr.map(function(data, i){
return(
<View style={[styles.cardContainer, styles.modularBorder, styles.basePadding]} key={i}>
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress().bind(this)}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
<View style={styles.cardContentRight}>
<Text style={styles.restData}>{i}</Text>
<View style={styles.gpsDataContainer}>
<Text style={styles.gpsData}>
{Number(data.lat).toFixed(2)}</Text>
<Text style={styles.gpsData}>{Number(data.long).toFixed(2)}</Text>
</View>
<Text style={styles.gpsData}>
{this.calcRow(55,55).bind(this)}
</Text>
</View>
</View>
);
});
}
render = ()=> {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return(
<View>
{this.renderGPSDataFromServer()}
</View>
)
}};
How do I go about fixing this and in this case what is the problem?
this.props are read-only
React docs - component and props
And therefore a component shouldn't try a to modify them let alone mutate them as you are doing here:
_buttonPress = () => {
this.props.navigator.push({
id: 'Main'
})
}
I'd suggest using state instead:
_buttonPress = () => {
this.setState = {
...this.state,
navigator: {
...this.state.navigator,
id: 'Main'
}
}
}
Regarding your binding issue:
the .map method takes a 2nd argument that is used to set the value of this when the callback is invoked.
In the context of your question, you just need to pass thisas the 2nd argument to you .map method to bind the components scope's this to it.
This is happening because, the function inside the map method creates a different context. You can use arrow functions as the callback in the map method for lexical binding. That should solve the issue you are having.
renderGPSDataFromServer =() => {
const {loaded} = this.state;
const {state} = this.state;
return this.state.dataArr.map((data, i) => {
return(
<View style={[styles.cardContainer, styles.modularBorder, styles.basePadding]} key={i}>
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress().bind(this)}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
<View style={styles.cardContentRight}>
<Text style={styles.restData}>{i}</Text>
<View style={styles.gpsDataContainer}>
<Text style={styles.gpsData}>
{Number(data.lat).toFixed(2)}</Text>
<Text style={styles.gpsData}>{Number(data.long).toFixed(2)}</Text>
</View>
<Text style={styles.gpsData}>
{this.calcRow(55,55).bind(this)}
</Text>
</View>
</View>
);
});
}
Also, once you've used arrow functions in the class function definition you
don't need to bind them in constructor like:
constructor(props) {
super(props);
this._customMethodDefinedUsingFatArrow = this._customMethodDefinedUsingFatArrow.bind(this)
}
Also, once you have defined class functions as arrow functions, you
don't need to use the arrow functions while calling them either:
class Example extends React.Component {
myfunc = () => {
this.nextFunc()
}
nextFunc = () => {
console.log('hello hello')
}
render() {
// this will give you the desired result
return (
<TouchableOpacity onPress={this.myFunc} />
)
// you don't need to do this
return (
<TouchableOpacity onPress={() => this.myFunc()} />
)
}
}
not sure if this is the problem, but I think is code is wrong, and may be potentially causing your issue.
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress().bind(this)}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
specifically this line onPress={this._buttonPress().bind(this)}>
you are invoking the function and binding it at the same time.
The correct way to do this would be so
onPress={this._buttonPress.bind(this)}>
this way the function will be called only onPress.
You are going in the right direction, but there is still a minor issue. You are passing a function to your map callback that has a different scope (this) than your component (because it is not an arrow function), so when you do bind(this), you are rebinding your callback to use the scope from map. I think this should work, it basically turns the callback that you pass to map into an arrow function. Also, since you bind your function in the constructor, you do not need to do it again:
// The constructor logic remains the same
// ....
renderLoadingView() {
return (
<View style={[styles.cardContainer, styles.loading]}>
<Text style={styles.restData}>
Loading ...
</Text>
</View>
)
}
_buttonPress = () => {
this.props.navigator.push({
id: 'Main'
})
}
renderGPSDataFromServer =() => {
const {loaded} = this.state;
const {state} = this.state;
return this.state.dataArr.map((data, i) => {
return(
<View style={[styles.cardContainer, styles.modularBorder, styles.basePadding]} key={i}>
<View style={styles.cardContentLeft}>
<TouchableHighlight style={styles.button}
onPress={this._buttonPress}>
<Text style={styles.restData}>View Video</Text>
</TouchableHighlight>
</View>
<View style={styles.cardContentRight}>
<Text style={styles.restData}>{i}</Text>
<View style={styles.gpsDataContainer}>
<Text style={styles.gpsData}>
{Number(data.lat).toFixed(2)}</Text>
<Text style={styles.gpsData}>{Number(data.long).toFixed(2)}</Text>
</View>
<Text style={styles.gpsData}>
{this.calcRow(55,55).bind(this)}
</Text>
</View>
</View>
);
});
}
render = ()=> {
if (!this.state.loaded) {
return this.renderLoadingView();
}
return(
<View>
{this.renderGPSDataFromServer()}
</View>
)
}};