TabBarIOS: onlyChild must be passed a children with exactly one child - javascript

I am new to react-native and I have an error trying to create TabBarIOS.
It keeps telling me onlyChild must be passed a children with exactly one child but my code is exactly identical with the tutorial I followed.
index.ios.js
'use strict';
var React = require('react-native');
var Welcome = require('./welcome.js');
var More = require('./more.js');
var {
Alert,
AppRegistry,
StyleSheet,
Text,
View,
Component,
TabBarIOS
} = React;
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#f5fcff',
alignItems: 'center'
},
button: {
height: 44,
flexDirection: 'row',
backgroundColor: '#488bec',
alignSelf: 'stretch',
justifyContent: 'center',
},
buttonText: {
fontSize: 18,
fontFamily: 'Helvetica',
color: 'white',
alignSelf: 'center',
}
});
class iCareReactNative extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: 'welcome'
};
}
render() {
return (
<TabBarIOS selectedTab={this.state.selectedTab}>
<TabBarIOS.Item
selected={this.state.selectedTab === 'welcome'}
systemIcon="featured"
onPress={() => {
this.setState({
selectedTab: 'welcome'
});
}}>
<Welcome/>//welcome component
</TabBarIOS.Item>
<TabBarIOS.Item
selected={this.state.selectedTab === 'more'}
systemIcon="contacts"
onPress={() => {
this.setState({
selectedTab: 'more'
});
}}>
<More/>//more component
</TabBarIOS.Item>
</TabBarIOS>
)
}
}
AppRegistry.registerComponent('iCareReactNative', () => iCareReactNative);
welcome.js
'use strict';
var React = require('react-native');
var {
StyleSheet,
View,
Text,
Component
} = React;
var styles = StyleSheet.create({
description: {
fontSize: 20,
textAlign: 'center',
color: '#FFFFFF'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#123456'
}
});
class Welcome extends Component {
render() {
return(
<View style={styles.container}>
<Text style={styles.description}>
Welcome to Welcome
</Text>
</View>
);
}
}
module.exports = Welcome;
I found that usually this error will be triggered by TouchableHighlight LINK but I didn't use any of those.
Any idea?

This error is triggered when a component expects a single valid React element as a child and doesn't get one. To debug this issue, you will generally look for a component that hasn't been properly imported, or whose render method returns null.
In your case, the component expecting a child and not getting one is the selected TabBarIOS.Item. If you set both selected props of your TabBarIOS.Item components to false, you'll see that the error disappears.
Now why doesn't it find the Welcome and More components ? Because of the comment in your render method (//welcome component, //more component). Remove the comments and the error will be solved.
I'm not sure, though, why the comments didn't trigger an exception : it should complain that some text isn't wrapped in a Text component. Maybe something to do with your react-native version.

Related

React native - view config getter callback for component 'input' must be a function (received 'undefined')

I am trying to use <TextInput/> but i get this error:
view config getter callback for component 'input' must be a function (received 'undefined'). Make sure to start component names with a capital letter
Here's the code:
export default function App() {
return(
<View style={styles.container}>
<View style={styles.header}>
<TextInput />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight,
backgroundColor: "#F7F7F7FF",
paddingTop: StatusBar.currentHeight,
alignItems: "center",
justifyContent: "center"
},
header: {
backgroundColor: "pink",
padding: 20,
},
});
The error occurs only when i use <TextInput/>
The problem is coming from the import.
import {TextInput} from "react-native-web";
This components (from react-native-web) isn't working in React Native

React Native, Cover (Overlay) the Requested Page with Loading Indicator and setTimeout() to Hide it

I have this code and it works fine to shows the overlay the requested page for 5sec and Hide to shows the requested page's contents, But when the Loader indicator disappeared its (red) background still there, how to hide the background too?
It has two part firsts one for creating Loading Indicator to be hidden after 5 sec.
Working example on Expo.io:
Live Demo -
This is requested page's code: (Please, notice has to be called from /screens)
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Loader from './screens/Loader';
export default function App() {
return (
<View style={styles.container}>
<Loader /> //<--- I put the Loader here
<Text style={styles.paragraph}>
This is the requested page should be be covered by indicator background (Red color) <br/ >
The Loader disappear after 5 sec.<br />
But, its background still there!!
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
And the Loader.js code is :
import React, { Component } from 'react';
import { ActivityIndicator, View, Text, TouchableOpacity, StyleSheet } from 'react-native';
class Loader extends Component {
state = { animating: true }
closeActivityIndicator = () => setTimeout(() => this.setState({
animating: false }), 5000)
componentDidMount = () => this.closeActivityIndicator()
render() {
const animating = this.state.animating
return (
<View style = {styles.container}>
<ActivityIndicator
animating = {animating}
color = '#bc2b78'
size = "large"
style = {styles.activityIndicator}/>
</View>
)
}
}
export default Loader
const styles = StyleSheet.create ({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 0,
position: 'absolute',
height: "100%",
width: "100%",
backgroundColor: 'red',
opacity: 0.7
},
activityIndicator: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})
The problem is you are always rendering the Loader and its always visible, so the easiest way to handle this would be to return null and hide the loader when its not necessary like below.
render() {
const animating = this.state.animating;
if(!this.state.animating)
return null;
return (
<View style = {styles.container}>
<ActivityIndicator
animating = {animating}
color = '#bc2b78'
size = "large"
style = {styles.activityIndicator}/>
</View>
)
}

react native - Passing navigation props from parent to child

I'm trying to create a simple-ish mobile app, but I'm pretty new to this. I've spent some time searching about the errors I'm getting. It seems like a common problem, but they all had the same/similar solutions but the solutions didn't work for me.
What is I'm trying to do? Right now the app is two pages, the home screen (Overview Cards) and the Add Card screen.
There's a button on the Overview Cards that takes you to Add Card.
Add Card allows you to fill out some TextInput boxes and
Add Card should allow you to press the save button and be taken back to the Overview Card screen and see the data you entered in the form.
However, I am getting stuck at Step 3. I am trying to make the Save button navigate the user back to Overview Cards, but there are simply errors instead.
Below is my code, the errors I'm getting, and then what I've tried.
App.js
import React from 'react';
import { StyleSheet, Text, TextInput, View, Button, TouchableOpacity, ShadowPropTypesIOS } from 'react-native';
import AddCard from './components/AddCard.js';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { useNavigation } from '#react-navigation/native';
function HomeScreen({ navigation }) {
return (
<View style={styles.homeContainer}>
<Button title="Add Card" onPress={() => navigation.navigate('Add Card')}/>
{/* <Text value={this.props.inputValFT}/> */}
<Text style={styles.textStyle} >VISA xxxx</Text>
<Text style={styles.textStyle}>MASTERCARD xxxx</Text>
<Text style={styles.textStyle}>AMEX xxxx</Text>
</View>
);
}
function AddCardScreen() {
return (
<View style={styles.addCardContainer}>
<AddCard navigation={this.props.navigation} /> // **HERE**
</View>
);
}
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Overview Cards' }} />
<Stack.Screen name="Add Card" component={AddCardScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// function AddCardButton(){
// return (
// <View style={styles.buttonContainer}>
// <TouchableOpacity>
// <Text style={styles.button}>Add Card</Text>
// </TouchableOpacity>
// </View>
// );
// }
export default App;
const styles = StyleSheet.create({
homeContainer: {
flex: 1,
backgroundColor: '#ef95b1',
alignItems: 'center',
justifyContent: 'flex-start',
},
addCardContainer: {
flex: 1,
backgroundColor: '#28cdf0',
justifyContent: 'flex-start',
},
buttonContainer: {
flexDirection: 'row',
alignSelf: 'flex-end',
marginTop: 15,
},
button: {
flexDirection: 'row',
alignSelf: 'flex-end',
marginTop: 15,
right: 10,
backgroundColor: '#2565ae',
borderWidth: 1,
borderRadius: 12,
color: 'white',
fontSize: 15,
fontWeight: 'bold',
overflow: 'hidden',
padding: 10,
textAlign:'center',
},
textStyle: {
padding: 10,
}
});
Navigation.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import AddCardScreen from './AddCard';
const RootStack = createStackNavigator(
{
Home: HomeScreen,
AddCard: AddCardScreen,
},
{
initialRouteName: 'Home',
}
);
const AppContainer = createAppContainer(RootStack);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ef95b1',
alignItems: 'center',
justifyContent: 'flex-start',
},
textStyle: {
padding: 10,
}
});
export default createAppContainer(Navigation);
AddCard.js
import React, { Component } from 'react';
import { StyleSheet, View, Text, TextInput, TouchableOpacity } from 'react-native';
import { Input } from 'react-native-elements'
import { ScrollView } from 'react-native-gesture-handler';
// import { loadSettings, saveSettings } from '../storage/settingsStorage';
class AddCardScreen extends Component {
constructor(props) {
super(props);
this.state = {
firstTwo : '',
lastFour : '',
recentAmt : ''
};
this.addFT = this.addFT.bind(this)
this.addLF = this.addLF.bind(this)
this.addRecAmt = this.addRecAmt.bind(this)
}
static navigationOptions = {
title: 'Add Card'
};
addFT(firstTwo) {
this.setState(Object.assign({}, this.state.firstTwo, { firstTwo }));
}
addLF(lastFour) {
this.setState(Object.assign({}, this.state.lastFour, { lastFour }));
}
addRecAmt(recentAmt) {
this.setState(Object.assign({}, this.state.recentAmt, { recentAmt }));
}
// handleSubmit() {
// alert('New card saved. Returning to Home to view addition.');
// navigation.navigate('Home')
// } // firstTwo, lastFour, recentAmt
render() {
const {navigation} = this.props;
return (
<ScrollView>
<View style={styles.inputContainer}>
<Text h1> "Add a new card!" </Text>
<TextInput
style={styles.textInput}
placeholder="First two digits of card"
placeholderTextColor="#000000"
keyboardType={'number-pad'}
maxLength = {2}
onChangeText={this.addFT}
inputValFT={this.state.firstTwo}
/>
<TextInput
style={styles.textInput}
placeholder="Last four digits of card"
placeholderTextColor="#000000"
keyboardType={'number-pad'}
maxLength = {4}
onChangeText={this.addLF}
inputValLF={this.state.lastFour}
/>
<TextInput
style={styles.textInput}
placeholder="Most recent dollar amount"
placeholderTextColor="#000000"
keyboardType={'decimal-pad'}
onChangeText={this.addRecAmt}
inputValRA={this.state.recentAmt}
/>
</View>
<View style={styles.inputContainer}>
<TouchableOpacity
style={styles.saveButton}
onPress={() => navigation.navigate('Home')}> // ** HERE 2 **
<Text style={styles.saveButtonText}>Save</Text>
</TouchableOpacity>
</View>
</ScrollView>
);
}
}
// this.handleSubmit.bind(this)
export default AddCardScreen;
const styles = StyleSheet.create({
inputContainer: {
paddingTop: 15
},
textInput: {
borderColor: '#FFFFFF',
textAlign: 'center',
borderTopWidth: 1,
borderBottomWidth: 1,
height: 50,
fontSize: 17,
paddingLeft: 20,
paddingRight: 20
},
saveButton: {
borderWidth: 1,
borderColor: '#007BFF',
backgroundColor: '#007BFF',
padding: 15,
margin: 5
},
saveButtonText: {
color: '#FFFFFF',
fontSize: 20,
textAlign: 'center'
}
});
The errors I'm getting:
In App.js you can see the ** HERE ** that I put in. When I try to run this, the app loads fine until I click the "Add Card" button. I get this error: undefined is not an object (evaluating 'this.props.navigation').
If I take the navigate={this.props.navigation} part out from App.js, the app loads as it's meant to again, but this time I can click the "Add Card" button and reach the next screen with no issue. I fill out the form (TextInput parts in AddCard.js), but when I click the "Save" button, the app crashes. The error is: TypeError: undefined is not an object (evaluating 'navigation.navigate'). Most likely because of what I'm doing with onPress where it says ** HERE 2 ** in AddCard.js. handleSubmit() is currently commented out, but it used to be inside the onPress.
What I've tried:
Some of the answers I saw were that I need to pass in navigation from the parent to the child and that will make it work. By trying that, I get the errors I mentioned earlier. I also saw that someone mentioned using "withNavigation" or "useNavigation" which was supposed to allow the child to access navigation, but that didn't work for me either. Below are some of the links that I was trying to follow.
How do I pass navigation props from parent component to header?
Pass navigation.navigate to Child Component
https://reactnavigation.org/docs/use-navigation/
Thank you for reading, hopefully my explanation is clear enough.
I think your problem is somewhere here:
function AddCardScreen({ navigation }) {
return (
<View style={styles.addCardContainer}>
<AddCard navigation={navigation} />
</View>
);
}
There is no this, you're not in a class component, therefore this doesn't exists
The prop you are trying to pass should be called navigation and not navigate, since that's how you try to access it in the child component.
The navigation prop needs to be destructured inside the function argument function AddCardScreen({ navigation }), same as you already do for HomeScreen.

Updating State not updating view

I am currently learning React Native.
I have built a view which should show a text Loading when the data is being fetched and should display the data once the data has been received by the app.
The app is hitting the backend server and is getting the URL(tried console.error on app and it does popup with ared screen showing the correct data details ).
The problem is the view is not being updated. Another thing that is weird is that the conditional view is showing the border which has been set for the container, but it does not display any text or data except that. Even if I put the correct data. Not sure if the condition is being validated correctly or not.
The border I mentioned is set in articleContainer in the stylesheet and is attached with the container which is with the condition this.state.hasResult
If i remove the style from that view the border dissappears obviously but even putting a static text inside the view is not being displayed(checked incase I parsed the json wrong.) Seems a bit confusing.
var constants = require("../constants")
var React = require('react');
var ReactNative = require('react-native');
var t = require('tcomb-form-native');
var authenticate = require("../services/authenticate")
var {
AppRegistry,
AsyncStorage,
StyleSheet,
Text,
View,
TouchableHighlight,
Alert,
ListView,
Image,
} = ReactNative;
const options = {}
class RecipePage extends React.Component{
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1.id !== r2.id});
var getData = require("../services/get_featured");
var par = this;
var recipeId = props.recipeId;
this.state ={
hasResult: false,
noResult: false,
result: null,
isLoading: true,
}
getData(recipeId).then(function(data){
par.setState(
{
result:data,
hasResult:true,
noResult:false,
isLoading:false
}
)
}).catch(function(error) {
console.error(error);
});
}
goBack(){
this.props.navigator.pop();
}
render() {
return (
<View style={styles.container}>
<View style={styles.row}>
<Text style={styles.title}>Recipe</Text>
<TouchableHighlight onPress={this.goBack.bind(this)}>
<Image style={styles.back}
source={require("../static/images/back.png")}
/>
</TouchableHighlight>
</View>
{
this.state.hasResult &&
<View style={styles.article_container} >
<Text style={styles.article_title} >{this.state.result.id}</Text>
<Image style={styles.article_img}
source={{uri: this.state.result.image_link}}
/>
</View>
}
{
this.state.isLoading &&
<View style={styles.article_container} >
<Text style={styles.article_title} >Loading</Text>
</View>
}
</View>
);
}
}
var styles = StyleSheet.create({
container: {
justifyContent: 'center',
marginTop: 25,
padding: 20,
backgroundColor: '#ffffff',
},
title: {
fontSize: 30,
alignSelf: 'center',
marginBottom: 30
},
article_container: {
justifyContent: 'center',
marginTop: 5,
padding: 20,
backgroundColor: '#ffffff',
borderBottomColor: '#555555',
borderBottomWidth: 1,
flex:1
},
article_title: {
fontSize: 25,
alignSelf: 'center',
marginBottom: 3,
flex:2
},
article_img: {
width:200,
height:200,
alignSelf: 'center',
flex:1
},
article_description :{
fontSize:15,
flex:3
},
back:{
backgroundColor:'#ffffff',
width:20,
height:20
}
});
module.exports = RecipePage;
If you think I can improve the code not relating to this question, then feel free to comment and enlighten me :)
Edit:
I played around a bit more and found that if I change the attached styles as:-
<View style={styles.row} >
<Text style={styles.title} >{this.state.result.title}</Text>
<Image
source={{uri: this.state.result.image_link}}
/>
</View>
from
<View style={styles.article_container} >
<Text style={styles.article_title} >{this.state.result.id}</Text>
<Image style={styles.article_img}
source={{uri: this.state.result.image_link}}
/>
</View>
I am able to view the title after making this change, but image is still not displayed. And I am not sure why it was not showing for the other style.
Even if one of the style is attached -> article_title or article_container, its not being displayed.
Edit 2:
Even if I apply the style manually, it is not displayed like
<Text style={{fontSize: 25,alignSelf: 'center',marginBottom: 3,flex:2}} >{this.state.result.title}</Text>
I am navigating to this page from a parent which uses similar styles for a listview and it is showing there perfectly.
The problem seemed to be that I used flex property in styles without using ScrollView or ListView.
I would research more into that and update the answer.

React Native Tabbar iOS binding issue [Expected a component class, got object Object]

I've got a simple React Native app with a TabBarIOS component.
For each tab, I have a seperate .js file and the main TabBarIOS component lives in index.ios.js. The other classes are home.ios.js and contact.ios.js.
After I click on an icon I want the app to show the corresponding page( home.ios.js or contact.ios.js). However when I click one of the icons, I get a "Expected a component class, got object Object" error.
So far it looks like the rendering of the TabBarIOS component is alright. I myself think that there is a problem in the binding with the other .js files or a problem with the injection.
index.ios.js
var React = require('react');
var ReactNative = require('react-native');
import Home from './home.ios';
import Contact from './contact.ios';
var {
AppRegistry,
TabBarIOS,
} = ReactNative;
var {
Component
} = React;
class Tab extends Component {
constructor(props) {
super(props);
this.state = {
selectedTab: 'Home'
};
}
render() {
return (
<TabBarIOS selectedTab={this.state.selectedTab}>
<TabBarIOS.Item
selected={this.state.selectedTab === 'Home'}
systemIcon="featured"
onPress={() => {
this.setState({
selectedTab: 'Home',
});
}}>
<home/>
</TabBarIOS.Item>
<TabBarIOS.Item
selected={this.state.selectedTab === 'Contact'}
systemIcon="contacts"
onPress={() => {
this.setState({
selectedTab: 'Contact',
});
}}>
<contact/>
</TabBarIOS.Item>
</TabBarIOS>
)
}
}
AppRegistry.registerComponent('ProtoReactNative', () => Tab);
home.ios.js (I include only this one, contact.ios.js looks the same)
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
StyleSheet,
View,
Text
} = ReactNative;
var {
Component
} = React;
var styles = StyleSheet.create({
description: {
fontSize: 20,
textAlign: 'center',
color: '#FFFFFF'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'blue',
}
});
class Home extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.description}>
Welcome to your React Native Start Component!
</Text>
</View>
);
}
}
module.exports = Home;
Hopefully someone has a solution for me!
Thanks in advance!
EDIT:
'use strict';
var React = require('react');
var ReactNative = require('react-native');
var {
StyleSheet,
} = ReactNative;
var {
View,
Text,
Component
} = React;
var styles = StyleSheet.create({
description: {
fontSize: 20,
textAlign: 'center',
color: '#FFFFFF'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'red',
}
});
class Contact extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.description}>
This could be your second tab
</Text>
</View>
);
}
}
module.exports = Contact;
Step 1: Change <home/> to <Home/> and <contact/> to <Contact/>
Step 2: Inside contact js file, View and Text should be imported from ReactNative. Currently its imported from React

Categories