How to reuse a UI element in react native - react-native

App.js
import React,{ Component } from 'react';
import { StyleSheet,TextInput, View, Button, Text } from 'react-native';
import Toast from 'react-native-simple-toast';
import TextItem from '.src/Components/Textviews/TextViewDisplay';
export default class App extends Component {
state = {
placeName : "",
titleText: "Text view"
}
placeNameChangeHandler = val =>{
this.setState({
placeName : val
})
}
placeSubmitHandler = () =>{
this.setState({
titleText: this.state.placeName.trim()
})
Toast.showWithGravity('This is a long toast at the top.', Toast.LONG, Toast.TOP)
}
render() {
return (
<View style={styles.rootContainer}>
<View style={styles.btnEditContainer}>
<View style ={styles.wrapStyle}>
<TextInput
style = {styles.textInputStyle}
value = {this.state.placeName}
onChangeText = {this.placeNameChangeHandler}
/>
</View>
<View style ={styles.wrapStyle}>
<Button
title="Add"
style ={styles.buttonStyle}
onPress ={this.placeSubmitHandler}/>
</View>
</View>
<View style={styles.textContainer}>
<View style ={styles.wrapStyle}>
<Text
style ={styles.textStyle}>
{this.state.titleText}
</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
rootContainer: {
height:"100%",
width:"100%",
backgroundColor: "#008000",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
btnEditContainer: {
backgroundColor:"#008080",
flexDirection:"row",
alignItems:"center",
justifyContent: "center"
},
textContainer: {
backgroundColor:"#00FFFF",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
textStyle: {
fontSize: 20,
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
buttonStyle: {
//flex:1
},
textInputStyle: {
borderColor:"black",
borderWidth:1,
},
wrapStyle: { marginLeft:5,
marginRight:5 },
});
TextViewDisplay.js
import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
const textItem = (props) => (
<View style={styles.rootElement}>
<Text style={styles.textItem}>
{props.textItem}
</Text>
</View>
);
const styles = StyleSheet.create({
rootElement : {
backgroundColor:"red"
},
textItem : {
color: '#f44336'
}
});
export default textItem;
What I am trying to do:
Instead of Text in App.js file I have written a
TextViewDisplay.js file to reuse.
How to properly implement in render function of App.js

If I undrestood your query correctly, then you just want to replace this
<Text style ={styles.textStyle}>
{this.state.titleText}
</Text>
from your App component with your TextItem (re-usable) component.
As you have already imported your TextItem component, you can do this way
<View style={styles.textContainer}>
<View style ={styles.wrapStyle}>
//This is your re-usable component
<TextItem style = {styles.textStyle}>
{this.state.titleText}
</TextItem >
</View>
</View>
And you just need to change {props.textItem} to {props.children} in your TextItem component.
<View style={styles.rootElement}>
<Text style={{...props.style, ...styles.textItem}}> //Here you can get style from your parent component
{props.children} //This is the child element
</Text>
</View>
Note: Always use PascalCase names for your component.
If you don't want to work with {props.children} and only want to work with {props.textItem}, in that case you need to pass props,
<TextItem style = {styles.textStyle} textItem = {this.state.titleText} />
Now you can use {props.textItem},
<View style={styles.rootElement}>
<Text style={{...props.style, ...styles.textItem}}> //Here you can get style from your parent component
{props.textItem}
</Text>
</View>

Just import your component and instantiate it in any render function:
import textItem from './TextViewDisplay';
render() {
return (
<View style={styles.rootContainer}>
<textItem textItem={'label'} />
...
</View>
}
I would suggest defining the component upper camel case though:
const MyTextItem = (props) => (...)
And giving the property a more meaningful name:
<MyTextItem label={'label'} />
Finally, it's also good to define property types, so other users of your component known which properties to pass, and what type they should be. For example as such:
import PropTypes from 'prop-types';
MyTextItem.propTypes = {
label: PropTypes.string.isRequired,
}

It's very simple re using components in react native :
Step - 1 : Import the function to the screen where you want to reuse that.
ex : import {textItem} from '../pathName'
Step -2 :
render() {
return (
//Create the component of the UI and then use it in render and don't forget to import that:
<TextItem
textItem = 'happy Coding'
/>
)}
Also make sure the component name starts with capital letter.

Related

How to change only the active button background color [React-Native]

import React, { Component } from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
class App extends Component {
constructor(props){
super(props)
this.state={
selected:null
}
}
handle=()=>{
this.setState({selected:1})
}
render() {
return (
<View>
<TouchableOpacity style={[styles.Btn, {backgroundColor:this.state.selected===1?"green":"white"}]} onPress={this.handle}>
<Text style={styles.BtnText}>Button 1</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.Btn} onPress={this.handle}>
<Text style={styles.BtnText}>Button 2</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.Btn} onPress={this.handle}>
<Text style={styles.BtnText}>Button 3</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
Btn: {
borderWidth: 1,
width: 100,
height: 20,
borderRadius: 8,
margin: 5,
padding: 10,
justifyContent: 'center',
flexDirection: 'row',
alignItems: 'center',
},
BtnText: {
fontSize: 15,
},
});
export default App;
Snack Link : https://snack.expo.dev/U_fX-6Tao-
I want to make it so when I click a button, the active button backgroundColor should change to "green" and text to "white" and the rest of the buttons backgroundColor and textColor should stay "red". But when I click another button then that button should become active and the previous active button should get back to its normal state.
It would be wonderful if you could also explain the logic behind it as I'm a newbie in React Native.
Thank you.
You are always setting the active button to the first one. Also, I would use an array to render the buttons. I would do something like this:
class App extends Component {
constructor(props){
super(props)
this.state = {
selected: null
}
}
handlePress = (item) => {
this.setState({ selected: item })
}
render() {
return (
<View>
{[...Array(3).keys()].map((item) => {
return (
<TouchableOpacity key={item} style={[styles.Btn, {backgroundColor: this.state.selected === item ? "green" : "white" }]} onPress={() => this.handlePress(item)}>
<Text style={styles.BtnText}>Button {item + 1}</Text>
</TouchableOpacity>
)
})}
</View>
);
}
}
I created an Themed component(OK I did not create it. It is there when I create the app with Expo).
import { useState } from 'react';
import { TouchableOpacity as DefaultTouchableOpacity } from 'react-native';
export type TouchableProps = DefaultTouchableOpacity['props'] & { activeBgColor?: string };
export function TouchableOpacity(props: TouchableProps) {
const [active, setActive] = useState(false);
const { style, activeBgColor, ...otherProps } = props;
if (activeBgColor) {
return (
<DefaultTouchableOpacity
style={[style, active ? { backgroundColor: activeBgColor } : {}]}
activeOpacity={0.8}
onPressIn={() => setActive(true)}
onPressOut={() => setActive(false)}
{...otherProps}
/>
);
}
return <DefaultTouchableOpacity style={style} activeOpacity={0.8} {...otherProps} />;
}
Then I use this TouchableOpacity everywhere.
<TouchableOpacity
style={tw`rounded-sm h-10 px-2 justify-center items-center w-1/5 bg-sky-400`}
activeBgColor={tw.color('bg-sky-600')}
>
<Text style={tw`text-white font-bold`}>a Button</Text>
</TouchableOpacity>
Oh I am writing TailwindCSS with twrnc by the way. You will love it.
See the screenshot below.

React Native last child selector not working

I'm trying to use the last child selector remove the bottom border from the last Text tag here.
I've used both the EStyleSheet and the StyleSheet but it doesn't seem to be working – the last Text tag still has a bottom border.
I've wrapped the Text tags in View and applied the 'opt' style to the View instead and that also doesn't work.
What am I doing something wrong?
import React from 'react';
import EStyleSheet from 'react-native-extended-stylesheet';
import {
Text,
View,
Image,
} from 'react-native';
EStyleSheet.build()
const styles = EStyleSheet.create({
container:{
flex:1,
},
options:{
},
opt:{
padding:5,
fontSize:25,
borderWidth:1,
borderColor:'black',
},
'opt:last-child': {
borderBottomWidth:0,
}
});
const Settings = () => {
return <View style={styles.container}>
<View style={styles.options}>
<Text style={styles.opt}>Edit profile</Text>
<Text style={styles.opt}>Preferences</Text>
<Text style={styles.opt}>Account settings</Text>
</View>
</View>
};
export default Settings;
To accomplish an item-order aware styling, try something that makes use of EstyleSheet.child like this way:
const items = ['Edit profile', 'Preferences', 'Account settings'];
const Settings = () => (
<View style={styles.container}>
<View style={styles.options}>
{items.map((text, i) => {
const style = EStyleSheet.child(styles, 'opt', i, items.length);
return <Text style={style}>{text}</Text>;
})}
</View>
</View>
);

React native layout misbehaving

I am trying to learn React native with Ignite. Been fighting with the layout.
Here is my main container render function:
render () {
return (
<View style={styles.mainContainer}>
<Image source={Images.background} style={styles.backgroundImage} resizeMode='stretch' />
<View style={[styles.container]}>
<View style={styles.section} >
{/* <Image source={Images.ready} />*/}
<Text style={styles.sectionText}>
Tap to randomly choose your training task. Slack off for 5
</Text>
</View>
<View style={styles.centered}>
<TouchableOpacity onPress={this._onPressButton}>
<Image source={Images.launch} style={styles.logo} />
</TouchableOpacity>
</View>
</View>
<View style={[styles.bottom]}>
<View >
<BottomBar />
</View>
</View>
</View>
)
}
In particular, the last sibling of the container has a view with a BottomBar component.The bottom style does this:
bottom: {
justifyContent: 'flex-end',
marginBottom: Metrics.baseMargin
}
the BottomBar component:
import React, { Component } from 'react'
// import PropTypes from 'prop-types';
import { View, Text, TouchableOpacity } from 'react-native'
import styles from './Styles/BottomBarStyle'
import Icon from 'react-native-vector-icons/FontAwesome'
export default class BottomBar extends Component {
// // Prop type warnings
// static propTypes = {
// someProperty: PropTypes.object,
// someSetting: PropTypes.bool.isRequired,
// }
//
// // Defaults for props
// static defaultProps = {
// someSetting: false
// }
render () {
console.tron.log('rendering my component')
console.tron.log('Bottom bar styles: \n',styles)
return (
<View style={[styles.iconsContainer, styles.debugGreen]}>
<TouchableOpacity style={[styles.icons,styles.debugYellow]} onPress={()=>{console.tron.log('rocket')}} >
<Icon style={styles.icons} name='rocket' size={40} color='white' />
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={ ()=>{console.tron.log('send')} }>
<Icon style={styles.icons} name='send' size={40} color='white' />
</TouchableOpacity>
</View>
)
}
}
the styles associated with it:
import { StyleSheet } from 'react-native'
import DebugStyles from '../../Themes/DebugStyles'
import { Metrics } from '../../Themes/'
export default StyleSheet.create({
...DebugStyles,
iconsContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
height: 45,
borderRadius: 5,
marginHorizontal: Metrics.section,
marginVertical: Metrics.baseMargin
},
icons:{
height: 45
}
})
The issue I have, is that if I saw that bottomBar component for a Rounded button as such:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity, Text } from 'react-native'
import styles from './Styles/RoundedButtonStyles'
import ExamplesRegistry from '../Services/ExamplesRegistry'
// Note that this file (App/Components/RoundedButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.
// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample('Rounded Button', () =>
<RoundedButton
text='real buttons have curves'
onPress={() => window.alert('Rounded Button Pressed!')}
/>
)
console.tron.log('Rounded button style: ',styles)
export default class RoundedButton extends Component {
static propTypes = {
onPress: PropTypes.func,
text: PropTypes.string,
children: PropTypes.string,
navigator: PropTypes.object
}
getText () {
const buttonText = this.props.text || this.props.children || ''
return buttonText.toUpperCase()
}
render () {
console.tron.log('roundedButton styles:', styles)
return (
<TouchableOpacity style={styles.button} onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.getText()}</Text>
</TouchableOpacity>
)
}
}
with its styles:
import { StyleSheet } from 'react-native'
import { Fonts, Colors, Metrics } from '../../Themes/'
export default StyleSheet.create({
button: {
height: 45,
borderRadius: 5,
marginHorizontal: Metrics.section,
marginVertical: Metrics.baseMargin,
backgroundColor: Colors.fire,
justifyContent: 'center'
},
buttonText: {
color: Colors.snow,
textAlign: 'center',
fontWeight: 'bold',
fontSize: Fonts.size.medium,
marginVertical: Metrics.baseMargin
}
})
I get the expected view :
However, with my BottomBar component I get:
One thing to notice is that the debugGreen style is just a border that should wrap around my BottomBar component and it is shown flat, but the icons within it render lower, and the debugYellow styled box around the icon is shown around the icon as expected, just shifted a whole way down.
If your mainContainer's view is flex : 1 or height : 100%, you should divide the child's height by 8:2 or the flex by 8:2.
Example
<View style={styles.mainContainer}> // flex: 1
<View style={styles.container}> // flex : 0.8
...
</View>
<View style={styles.bottom}> // flex : 0.2
<BottomBar />
</View>
</View>

Blank Screen with react-native navigation with no error code

Please, look at the debugger and the screen for what could be a problem. However, the code is highly displayed below for your perusal.
More also, I aimed at navigating to another page based on the id of the selected content.
App.js
The App.js is where I defined my stackNavigator
import React, {Component} from 'react';
import { StyleSheet, Text, View} from 'react-native';
import Post from './components/Post';
import PostSingle from './components/PostSingle';
import { createStackNavigator, createAppContainer } from 'react-navigation';
const RootStack = createStackNavigator(
{
PostScreen: { screen: Post},
PostSingleScreen:{screen: PostSingle},
},
{
initialRouteName: "PostScreen"
}
);
const AppNavigator = createAppContainer(RootStack);
export default class App extends Component {
constructor(props) {
super(props);
};
render() {
return (
<View style={styles.container}>
<AppNavigator/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#F3F3F3',
}
});
Post.js
I have tried to delete alignItem = center. In fact I deleted my style to see if there is any blocking the screen from coming up.
import React, { Component } from 'react';
import {
ScrollView,
StyleSheet,
View,
Text,
InputText,
TouchableOpacity
} from 'react-native';
import axios from 'axios';
export default class Post extends Component{
constructor(props){
super(props);
this.state = {
posts: []
}
}
readMore = () => {
()=> this.props.navigation.navigate('PostSingleScreen');
debugger;
}
componentDidMount(){
axios.get(`http://localhost/rest_api_myblog/api/post/read.php`)
//.then(json => console.log(json.data.data[0].id))
.then(json => json.data.data.map(mydata =>(
{
title: mydata.title,
body: mydata.body,
author: mydata.author,
category_name: mydata.category_name,
id: mydata.id
}
)))
//.then(newData => console.log(newData))
.then(newData => this.setState({posts: newData}))
.catch(error => alert(error))
}
render(){
return (
<View>
<ScrollView style={styles.scrollContent}>
<View style={styles.header}>
<Text style={styles.headerText}>Gist Monger</Text>
</View>
{
this.state.posts.map((post, index) =>(
<View key={index} style={styles.container}>
<Text style={styles.display}>
Author: {post.author}
</Text>
<Text style={styles.display}>
Category: {post.category_name}
</Text>
<Text style={styles.display}>
Title: {post.title}
</Text>
<Text style={{overflow:'hidden'}}>
Id: {post.id}
</Text>
<TouchableOpacity style={styles.buttonContainer}
onPress = {() => this.readMore()}
>
<Text style={styles.buttonText}>
Read More
</Text>
</TouchableOpacity>
</View>
))
}
</ScrollView>
<View style={styles.footer}></View>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
flex: 1,
height:40,
marginTop:50,
marginBottom:10,
flexDirection: 'row',
justifyContent:'center',
},
display: {
margin: 3,
fontSize: 16
},
headerText: {
fontWeight: 'bold',
fontSize: 40,
color: '#6200EE'
},
container: {
backgroundColor:'#efefef',
padding: 20,
margin: 5,
borderRadius:20,
justifyContent: 'center',
alignItems: 'center'
},
footer: {
flex: 1,
backgroundColor:'#000',
marginBottom:50
},
buttonContainer:{
height: 30,
width: 200,
marginTop: 15,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 15,
backgroundColor:'#6200EE'
},
buttonText: {
alignContent: 'center',
color: 'white'
}
});
PostSingle.js
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text
} from 'react-native';
import axios from 'axios';
export default class Post extends Component{
constructor(props){
super(props);
}
render(){
return (
<View>
<Text>My text</Text>
</View>
);
}
}
const styles = StyleSheet.create({
});
I did not test this code, but try to add flex: 1 to your container style. the main containers/components don't stretch if you don't tell them to
const styles = StyleSheet.create({
container: {
backgroundColor: '#F3F3F3',
flex: 1,
}
});
also, to check if the components render (helps debugging where the problem is), write a console log in every component's componentDidMount. if they mount, but nothing is visible, it's most likely a CSS issue. If it wasn't, it would throw errors instead of blank screen
second issue is, when you navigate you need to have params with react-navigation. the syntax for it is like this:
this.props.navigation.navigate('PostSingleScreen', { params })
so, if you have { id: someId } in your params, in the component you navigated to you will have {this.props.navigation.state.params.id}. so basically those params are inside navigation.state.params where you navigate
Let me help you with your second question. First, it is easier as said by just specifying params in your navigation.
For instance,
readMore = (id) => {
this.props.navigation.navigate('PostSingleScreen', {id:id})
}
However, in your TouchableOpacity, onPress method i.e. onPress = {() => this.readMore(post.id)}
In your PostSingle.js
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
Button
} from 'react-native';
import axios from 'axios';
class PostSingle extends Component{
constructor(props){
super(props);
this.state = {
posts: []
}
}
componentDidMount() {
const id = this.props.navigation.state.params.id;
axios.get(`http://localhost/rest_api_myblog/api/post/read_single.php?id=${id}`)
.then(json => json.data)
.then(newData => this.setState({posts: newData}))
.catch(error => alert(error))
}
render(){
return (
<View style={styles.container}>
<Text style={styles.display}>
{this.state.posts.title}
</Text>
<Text style={styles.display}>
{this.state.posts.author}
</Text>
<Text style={styles.display}>
{this.state.posts.category_name}
</Text>
<Text style={styles.display}>
{this.state.posts.body}
</Text>
</View>
);
}
}
I hope it helps
i would suggest using flaltist for this, and not this.state.map. this should give you the same outcome
readMore(id){
//do whatever you want with the id
this.props.navigation.navigate('PostSingleScreen',{id:id}); //or pass it as a prop
debugger;
}
renderItem = ({ item, index }) => {
return (
<View key={index} style={styles.container}>
<Text style={styles.display}>
Author: {item.author}
</Text>
<Text style={styles.display}>
Category: {item.category_name}
</Text>
<Text style={styles.display}>
Title: {item.title}
</Text>
<Text style={{overflow:'hidden'}}>
Id: {item.id}
</Text>
<TouchableOpacity style={styles.buttonContainer}
onPress = {() => this.readMore(item.id)}
>
<Text style={styles.buttonText}>
Read More
</Text>
</TouchableOpacity>
</View>
);
};
render(){
return (
<View style={{flex:1}}>
<FlatList
style={{flex:1}}
data={this.state.posts}
renderItem={this.renderItem}
numColumns={1}
keyExtractor={(item, index) => item.id} //this needs to be a unique id
ListHeaderComponent = {
<View style={styles.header}>
<Text style={styles.headerText}>Gist Monger</Text>
</View>}
/>
<View style={styles.footer}/>
</View>
);
}
If you are using react-navigation 5.x then it might be possible that you are adding CSS
alignItems: "center"
to your root file i.e. App.js or index.js I just removed it and it starts working for me.

React Native OnPress issue i have been having

I am new to react native and have been doing this for a week or so. I just finished the tutorials for making an interactive buttons and work on it. But i am stuck on this. The app is really simple right now, just trying to make a form and add some trigger event using onPress on it.
Below is the portion of my code. I am simply lost for words why its not calling SubmitThisForm() on the onPress event.
Can you guys help me on this.
Thanks a lot.
import React, { Component } from 'react';
import {Container, Content, InputGroup,Button, View, Icon, Card,
CardItem, Text, Body} from 'native-base';
import {Input} from './common';
class LoginForm extends Component {
state = {email: '', password: ''};
SubmitThisForm() {
console.log("Can you see this");
}
render () {
return (
<Container>
<Content>
<Card style={styles.FormContainer}>
<CardItem>
<Body>
<InputGroup borderType="regular">
<Icon name="ios-mail-outline" style={{color:'#384850'}}/>
<Input
placeHolder="example#example.com"
value = {this.state.email}
onChangeText={email=>this.setState( { email })}
/>
</InputGroup>
<InputGroup borderType="regular">
<Icon name="lock" style={{color:'#384850'}}/>
<Input
secureTextEntry= {true}
placeHolder="password"
value = {this.state.password}
onChangeText={password=>this.setState( { password })}
/>
</InputGroup>
</Body>
</CardItem>
</Card>
<View style={styles.SignIn}>
<Button block warning onPress={ () => {this.SubmitThisForm()}}><Text>Sign In</Text></Button>
</View>
<View style={styles.SignUp}>
<Button block info><Text>Sign Up</Text></Button>
</View>
<View style={styles.SignIn}>
<Button block primary><Text>Forgot Password</Text></Button>
</View>
</Content>
</Container>
);
};
}
const styles = {
ErrorTextStyle: {
fontSize: 20,
alignSelf: 'center',
color: 'red'
},
FormContainer:{
marginTop:30,
shadowColor:'#000',
shadowOffset:{width:0,height:2},
shadowOpacity:0.1,
shadowRadius:2,
},
SignIn:{
marginTop:10,
flex:1,
alignSelf:'stretch',
},
SignUp:{
marginTop:40,
flex:1,
alignSelf:'stretch',
}
}
export default LoginForm
The include input component is like this:
import React from 'react';
import {Text, View, TextInput} from 'react-native';
const Input = ({ label,value,onChangeText,placeHolder,secureTextEntry }) => {
const {InputStyle,LabelStyle,ContainerStyle } = styles;
return (
<View style = {ContainerStyle}>
<TextInput
secureTextEntry = {secureTextEntry}
placeholder={placeHolder}
autoCorrect={false}
style = {InputStyle}
value={value}
onChangeText={onChangeText}
/>
</View>
);
};
const styles = {
InputStyle:{
color:'#000',
paddingRight:5,
paddingLeft:5,
fontSize:18,
lineHeight:30,
flex:2,
height:40
},
LabelStyle:{
fontSize:18,
paddingLeft:20,
flex:1,
},
ContainerStyle:{
height:40,
flex:1,
flexDirection:'row',
alignItems:'center'
}
}
export { Input };
You need to either use an Arrow function, or bind() SubmitThisForm to your Component.
You can either declare your method like:
SubmitThisForm = () => {
console.log('Can you see this?')
}
Or, you can bind() your function in the constructor by adding:
constructor() {
super()
this.SubmitThisForm = this.SubmitThisForm.bind(this)
}
If you do not bind this in your custom functions, this will equal undefined. When you use Arrow functions however, this is lexically scoped which means the context of this will be the enclosing context (LoginForm)