I am a react-native newbie. I am wondering if there is a "correct" way to develop a react-native app? As the documentation is still very insufficient, I am afraid I may develop it in the very incorrect manner, and I prefer correct the mistake now, rather than after the project expands. From what I understand from my previous experience, we should not combine all pages in a single .js file, but how can each component communicate with each other?
I am currently doing like this inside index.android.js:
import Login from './Login';
import Register from './Register';
import Home from './Home';
class TheProject extends Component {
renderScene (route, navigator) {
_navigator = navigator;
switch (route.index) {
case 'Login':
return (
<View style={styles.container}>
<Login navigator={navigator} />
</View>
);
case 'Register':
return (
<View style={styles.container}>
<Register navigator={navigator} />
</View>
);
}
}
render() {
return (
<Navigator
initialRoute={{index: 'Login'}}
renderScene={this.renderScene}
/>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-around',
backgroundColor: '#F0F0F0',
flexWrap:'wrap'
},
});
AppRegistry.registerComponent('TheProject', function() { return TheProject });
module.exports = TheProject;
and in my Login.js (other.js file will be similar):
export default class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
};
}
registerOnPress() {
this.props.navigator.push({
index: 'Register'
});
}
loginOnPress() {
if (this.state.username != '' && this.state.password != '') {
Alert.alert('Success!', 'Successfully logged in.');
this.props.navigator.push({
index: 'Home'
});
} else {
Alert.alert('Failed to log-in', 'Wrong username or password');
}
}
render () {
return (
<View style={styles.individualContainer}>
<View style={styles.content}>
<View style={styles.formField}>
<View style={styles.input}>
<Text style={styles.label}>Username : </Text>
<View style={styles.fieldBox}>
<TextInput
style={styles.field}
underlineColorAndroid={'transparent'}
onChangeText={(data) => this.setState({ username: data })}
/>
</View>
</View>
<View style={styles.input}>
<Text style={styles.label}>Password : </Text>
<View style={styles.fieldBox}>
<TextInput
style={styles.field}
underlineColorAndroid={'transparent'}
secureTextEntry={true}
onChangeText={(data) => this.setState({ password: data })}
/>
</View>
</View>
</View>
<View style={styles.input}>
<TouchableHighlight style={styles.buttonBox} onPress={this.loginOnPress.bind(this)}>
<Text style={styles.buttonText}>Login</Text>
</TouchableHighlight>
</View>
<View style={styles.input}>
<TouchableHighlight style={styles.buttonBox} onPress={this.registerOnPress.bind(this)}>
<Text style={styles.buttonText}>Register</Text>
</TouchableHighlight>
</View>
</View>
</View>
);
}
}
I know there are a lot of ways to develop, but am I on the right track? I am quite blurred when it comes to component mount and unmount.
Your question is very vague, phrased in such a way that the only real answer is "there is no single right answer".
I suggest looking at the Awesome React Native - Seeds list for good starter kits and the Examples list as well.
Communication between components is quite straight-forwarded and is covered by Facebook's documentation on the subject.
You should definitely not develop your entire application in a single file.
Keep in mind that you do not need "React native documentation" to write react native at the beginning. First you should understand the fundamentals of React because once you do, you'll notice that those fundamentals apply identically whether you're writing a web or native app, the only thing that differs are the components used.
At first I was about to vote for closing this question, but it's somehow fascinating and tech-related if we just look through the right glasses :)
At first, you should note you have a lot of nested <View> components. This could be re-structured, where both username and password label + textinput would be applied as a component - receiving text label and onChangeText as props.
Same goes with Login and Register buttons, create them as separate component, where you define title of the button and loginOnPress/registerOnPress functions as props.
Remember to use propTypes whenever needed. This helps you and other devs to keep on track what happens in different components and how they're related to each other. For instance: Login.propTypes = { navigator: PropType.func.isRequired}
Use Flow (http://flowtype.org) with you project for types and annotations.
Related
I have made a main screen in which I have added three button in the header, on pressing I want to open three different screens respectively but its not working.
Here's what I've tried:
constructor(props) {
super(props);
this.state = {
initialstate: 0, //Setting initial state for screens
};
}
render(){
return(
<View style={styles.container}>
<TouchableOpacity onPress={() => this.setState({ initialstate: 0})}>
<Image source={require('../../assets/add.png')}
resizeMode="contain"/>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.setState({ cardstate: 1})}>
<Image source={require('../../assets/request.png')}
resizeMode="contain"/>
</TouchableOpacity>
<TouchableOpacity onPress={() => this.setState({ cardstate: 2})}>
<Image source={require('../../assets/send.png')}
resizeMode="contain"/>
</TouchableOpacity>
{this.state.initialstate == 0 ? ( <RequestComp/> ) : ( <TopUpComp/> ) } //Over Here when I use the Third Screen like " : <SendComp/> " it gives me JXS error says "EXPECTED }"
</View>
The first problem is that you have an initialState state variable that is only updated by the first buttons and the other two are setting cardState so even if the ternary statement was formatted correctly it wouldn't have worked either way
But aside from this problem I don't recommend using a ternary for what you're trying to do, because the conditions become difficult to read.
There are multiple ways of doing this, but I like the approach of the accepted answer here React render various components based on three logic paths). The idea is to create an object that holds a mapping of strings to components. Then you can conditionally render an item based on the current key value.
Here's an example of how you could refactor your code to use this approach:
const tabComponents = {
request: <RequestComp />,
topUp: <TopUpComp />,
send: <SendComp />,
};
class CustomTabs extends React.Component {
constructor(props) {
super(props);
this.state = {
cardstate: 'request', // Setting initial state for screens
};
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity
onPress={() => this.setState({ cardstate: 'request' })}>
// Button content...
</TouchableOpacity>
<TouchableOpacity onPress={() => this.setState({ cardstate: 'topUp' })}>
// Button content...
</TouchableOpacity>
<TouchableOpacity
onPress={() => this.setState({ cardstate: 'send' })}>
// Button content...
</TouchableOpacity>
{tabComponents[this.state.cardstate]}
</View>
);
}
}
so I wanted to use this.props.navigation.navigate() and I got an error message saying undefined.
after reading here in stockOverflow I saw that I needed declare a constructor like this
constructor(props) {
super(props);
}
however this keeps giving me an error saying ";" is was expected regardless of what I do, here is a simplified version of my code
const activityStyles = ActivitiesStyles.createStyles()
export default (props) => {
const {item: event, sensorID, homeInfo} = props
return (
<View style={activityStyles.linkContent} underlayColor={Colors.navigationBkgdActive}>
<View style={{flex: 0.60, flexDirection: 'row'}}>
<TouchableHighlight onPress={(event)=>{this.props.navigation.navigate("WalkThru")}}>
<SensorIcon style={iconStyle} size={Typography.bodyLineHeight} type={event.type} />
</TouchableHighlight>
<TextInput
placeholder={event.type}
autoCapitalize={true}
style={activityStyles.text}>
</TextInput>
</View>
</View>
)
}
As Emile Bergeron suggested, you should go with props since this keyword is available only for Class-Based components.
A class based component would be as follows.
export default class componentName extends Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
return (
<TouchableHighlight
<View style={activityStyles.linkContent} underlayColor={Colors.navigationBkgdActive}>
<TouchableHighlight onPress={(event)=>{this.props.navigation.navigate("WalkThru")}}>
<SensorIcon style={iconStyle} size={Typography.bodyLineHeight} type={event.type} />
</TouchableHighlight>
<TextInput
placeholder={event.type}
</TextInput>
</View>
</TouchableHighlight>
)
}
}
Furthermore, I can propose you look into differences between Stateless Functional Components and Class Components in React / React-Native.
So I am trying to build an app similar to a blog. And I am not sure how to render the single post that has been chosen to read.
<TouchableWithoutFeedback onPress={ () => this.props.navigation.navigate('Unique')}>
<Text style={styles.titleStyle}>{post.title}</Text>
</TouchableWithoutFeedback>
^This is the event that navigates me to the post I have clicked (not sure how to pass its props or id)
And this is the component I am redirected to:
class Page extends Component {
render(){
return(
<ScrollView>
<Query query={UniqueNews}>
{({data, loading})=> {
if(loading) return <Text>Loading</Text>;
const {posts} = data;
return posts.map(post=>
<OneNews key={post.id} post={post} />
)
}}
</Query>
</ScrollView>
)
}
}
const UniqueNews = gql `query{
posts{
title
author
date
id
body
image{
url
}
}
}
`
OneNews =
<View style={styles.containerStyle}>
<View style={styles.cardSectionStyle}>
<Text>{title}</Text>
<Text>{author}</Text>
<Text>{body}</Text>
</View>
</View>
When I render or navigate to that screen, every post is shown instead of the one I have tapped on. How do I set this up? My expected output is to just see one of the posts (being the chosen one by tapping on it) and render it. Thanks
I found a way to solve this:
First I will pass a param while navigating to the Unique component :
<TouchableWithoutFeedback onPress={ () => this.props.navigation.navigate('Unique', {id: post.id})}>
<Text style={styles.titleStyle}>{post.title}</Text>
</TouchableWithoutFeedback>
After that, I will simply make a conditional render inside that Unique component:
<ScrollView>
<Query query={singleNews}>
{({data, loading})=> {
if(loading) return <Text>Loading</Text>;
const {posts} = data;
return posts.map(post=>
{if(this.props.navigation.state.params.id===post.id)
return <View>
<OneNews key={post.id} post={post} />
</View>}
)}}
</Query>
</ScrollView>
And that's it.
I have simple app, with 3 tabs. Tabs are defined by TabBarIOS in index.ios. I’m not using Navigator nor NavigatorIOS. In each TabBarItem , I just put the component name in tags. Like this :
return(
<TabBarIOS
selectedTab={this.state.selectedTab}
//unselectedTintColor="yellow"
//tintColor="white"
//barTintColor="darkslateblue"
>
<TabBarIOS.Item
icon={{uri: 'courses.png', scale: 5}}
title="Courses"
badge={undefined}
selected={this.state.selectedTab === 'courses'}
onPress={() => {
this.setState({
selectedTab: 'courses',
});
}}>
<Courses />
</TabBarIOS.Item>
<TabBarIOS.Item
icon={{uri: 'register.png', scale: 5}}
title="Register"
badge={undefined}
selected={this.state.selectedTab === 'register'}
onPress={() => {
this.setState({
selectedTab: 'register',
});
}}>
<NavigatorIOS
//style={styles.nav}
initialRoute={{
title : 'Register',
component: Register
}}
/>
</TabBarIOS.Item>
<TabBarIOS.Item
icon={{uri: 'profile.png', scale: 5}}
title="Profile"
badge={undefined}
selected={this.state.selectedTab === 'profile'}
onPress={() => {
this.setState({
selectedTab: 'profile',
});
}}>
<Profile />
</TabBarIOS.Item>
</TabBarIOS>
);
If you see in code, in first and third tab, i'm showing content of the item by puting component name in tags like
<Courses />
But for second item, i tried using navigatorios to show component, but it shows only a blank page with title. it doesn't show the content of component. i see lots of examples working like that, but it didn't work for me. maybe because i'm not using navigator or navigaatorios for index.ios , because in most examples i see, they put navigatorios for index and set initialroute. i tried it, but didn't work for me.
Everything works fine till here. In Courses page, I have a list view with items under it (actually items with header, which are collapsible) , when user clicks on each item , I need to redirect user to a page to show that course’s details. (I should pass some arguments also).
render(){
return (
<View style={{
flex: 1
}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
style={styles.listView}
/>
</View>
);
}
renderRow(data) {
var header = (
<View>
<View style={styles.rowContainer}>
<View style={styles.textContainer}>
<Text style={styles.title}>{data.nid}</Text>
<Text style={styles.description} numberOfLines={0}>{data.title}</Text>
</View>
</View>
<View style={styles.separator}></View>
</View>
);
///////////
var cid = [];
var content = [];
for(let x=0; x < Object.keys(data.course).length; x++){
cid[x] = data.course[x].course_id;
content.push(
<TouchableHighlight
underlayColor='#e3e0d7'
key={x}
onPress={()=> {
this.rowPress(x); ///// here i need to redirect user to another page to show course's details
}}
style={styles.child}
>
<Text style={styles.child}>
{data.course[x].title}
</Text>
</TouchableHighlight>
);
}
var clist = (
<View style={styles.rowContainer}>
{content}
</View>
);
////////////
return (
<Accordion
header={header}
content={clist}
easing="easeOutCubic"
/>
);
}
I want to redirect user to a page and show details, and i want to have a header title and a back button (like Navigator or NavigatorIOS).
I've found a sample app that is doing exactly what i'm looking for, but it's too complicated for me, that i didn't understand how it is working,
react native tabbar-navigator (iOS)
I've looked into many examples and tutorials. i tried to implement their code into mine but it didn't work.
Also find some other tutorials using redux, but it's so complicated and hard for me. i'm looking for something more simpler.
Navigation using Redux
Looking forward to hearing from you,
Any help is highly appreciated. Thanks in Advance!
I found out that when i have few scenes and i want to navigate between them , best practice is to use Navigator or NavigatorIOS.
I continue my app and did few more scenes and components ( with few levels going deep ) and i found out that Navigator or NavigatorIOS is not working properly for that purpose. Then i switched to NavigationExperimental and that was awesome. Then i had some problems in state management, i found out about redux. it was not easy at first, but when i start it, you will love it.
So for newbies like me, it's better to start with redux at first. that would make life much easier for you :)
So Basically I am getting error in the title which is related to the navigator.
The error pops up when I press on the Icon.
What I basically want to do is make a Tab bar at the top that switches between three different views: feed, wiki, and message board
Here is my index.android.js: (imports Nav)
_renderScene(route, navigator) {
var globalNavigatorProps = {navigator};
switch(route.ident) {
case "FeedView":
return(
<Feed
{...globalNavigatorProps}
/>
);
case "WikiView":
return(
<View>
<Text>
{'Hello'};
</Text>
</View>
);
case "BoardView":
return(
<View>
<Text>
{'Hello'};
</Text>
</View>
);
default:
console.log(`Something went wrong ${route}`);
}
}
render(){
return(
<View>
<Nav />
<Navigator
initialRoute={{ident:"Feed"}}
ref="appNavigator"
renderScene={ this._renderScene }
/>
</View>
);
}
Here is my Nav.js:
constructor(props){
super(props);
}
render(){
console.log(this.props.navigator);
return(
<View style={{flexDirection: "column"}}>
<View style={styles.nav}>
<Icon onPress={(event) => this.props.navigator.push({ident: "Feed"})} name="newspaper-o" size={22}/>
<Icon name="wikipedia-w" size={22}/>
<Icon name="comments" size={22}/>
</View>
<View style={styles.divider}/>
</View>
);
}
_changeView(type){
}
I dont think this is the issue, but the renderScene function won't be bound to the react component.
Try turning renderScene={ this._renderScene } into renderScene={ this._renderScene.bind(this) }
If you are trying to implement a tabbed view there are better ways of doing it you can always use an open source module such as this one https://github.com/skv-headless/react-native-scrollable-tab-view
the developer has made it in such a way that it is easy to use and the example is good enough to get through most of the part.
So instead of creating such components yourself what I would suggest is to use the modules made by the community.
Hope my answer was helpful.
this error always occurs when you didnt bind(this),check you Icon module onPress method