ReactNative send style as props to component - react-native

I am new to learning React Native and I would like to build a (ReactNaive & Expo) application with a good interface look, How can I build a component and send style{backgroundColor} as props then render the component on Home
home.js
import React from 'react'
import { View, Text , Image , ScrollView} from 'react-native';
import { FlatList } from 'react-native-gesture-handler';
import { COLORS, SIZES,FONTS} from '../styles/theme.js';
import Category from '../components/Category.js';
import CategorySlider from '../components/CategorySlider'
const Home = () => {
function renderHeader(){
return(
<View
style = {{
flexDirection: 'row' ,
marginTop: 40,
marginBottom: 10,
paddingHorizontal:SIZES.padding,
alignItems : 'center'
}}
>
<View style={{flex:1}}>
<Text >Hello , Wafa</Text>
</View>
{/* Nonfiction Button */}
</View>
)}
return (
<View
style ={{
flex: 1,
backgroundColor: COLORS.white
}}
>
{/* HEADER */}
{renderHeader()}
{/* Content */}
<View style={{height:130 , marginTop:SIZES.margin}}>
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
>
<Category name='Balance' />
<Category name='Saving' style={COLORS.green} />
<Category name='Income' style={ COLORS.brown } />
<Category name='Loans' style={ COLORS.pink}/>
<Category name='Saving' style={ COLORS.pink}/>
<Category name='Saving' style={ COLORS.pink}/>
</ScrollView>
</View>
<CategorySlider/>
</View>
)
}
export default Home;
It is the component I want to render on home.js
Category.js
import React, { PureComponent } from 'react'
import { Text, View ,StyleSheet ,Image } from 'react-native'
import { backgroundColor } from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes';
import { COLORS, SIZES,FONTS} from '../styles/theme.js';
export default class Category extends PureComponent {
render() {
return (
<View style={{marginLeft: SIZES.margin ,marginRight: SIZES.margin}}>
<View>
<View style={{
height: 60,
width: 60,
backgroundColor:COLORS.pink,
borderRadius: SIZES.radius,
}}>
<View />
<View>
<Image />
</View>
</View>
<Text
style={{textAlign:'center'}}
>
{this.props.name}</Text>
</View>
</View>
)
}
}
l made also File for Design system contains style I will repeat on the application.
theme.js:
import { Dimensions } from "react-native";
import { useFonts } from 'expo-font';
const { width, height } = Dimensions.get("window");
export const COLORS = {
green: "#68AB9F",
brown: "#c18e62",
pink: "#d99e96" ,
gray: "#383e42",
white: "#f5f7fc",
}

Here you're working on one with a Functional based component (home.js) and one with Class-based (Category.js). Standard is if first convert your Category component with functional then you can save that props to your Category component like:
import React, { useState } from 'react'
import { Text, View ,StyleSheet ,Image } from 'react-native'
import { backgroundColor } from 'react-native/Libraries/Components/View/ReactNativeStyleAttributes';
import { COLORS, SIZES,FONTS} from '../styles/theme.js';
const Category = (props) => {
const [backgroundColor, setBackgroundColor] = useState(props.style || null); //store your prop in state like this
const [name, setName] = useState(props.name || null);
return (
<View style={{marginLeft: SIZES.margin ,marginRight: SIZES.margin}}>
<View>
<View style={{
height: 60,
width: 60,
backgroundColor: backgroundColor,
borderRadius: SIZES.radius,
}}>
<View />
<View>
<Image />
</View>
</View>
<Text
style={{textAlign:'center'}}
>
{name}</Text>
</View>
</View>
)
}
export default Category;
Hope this works for you.

In your parent component take your style object in the wrapper of child component i.e.
function Parent(){
return (
<Child cssdata={{backgroundColor:'red',marginLeft:8}}/>
)
}
and in your child component take this data from props and applied to it
function Child(props){
return (
<View style={props.cssdata}></View>
)
}

Related

array values showing undefined in react native expo while working in web

The code is working in web version of react native but not in expo The array values are showing unefined but they are working fine in web version.Please tell me what is wrong here
import { View, Text, StyleSheet } from "react-native"
import React from "react"
import { FontAwesomeIcon } from "#fortawesome/react-native-fontawesome"
import { faEdit, faMinusSquare } from "#fortawesome/free-regular-svg-icons"
function List({ todoArr, handleModalOpen, removeItem }) {
console.log(todoArr)
return (
<>
{todoArr.map((item, index) => {
console.log(index)
return (
<View>
<Text key={item} style={styles.item}>{item}</Text>
<Text onPress={() => handleModalOpen(index, item)}>
<FontAwesomeIcon icon={faEdit} />
</Text>
<Text onPress={() => removeItem(index)}>
<FontAwesomeIcon icon={faMinusSquare} />
</Text>
</View>
)
})}
</>
)
}
const styles = StyleSheet.create({
item: {
fontSize: 20
},
})
export default List
import { ScrollView, StyleSheet, Text, View } from 'react-native';
import Buttons from './components/Add and Del Btn';
import EditModal from './components/EditModal';
import Heading from './components/Heading';
import Input from './components/Input';
import List from './components/List';
import Todo from './todo';
export default function App() {
let [indexNo , inpVal,updInpVal , todoArr,modalVisible ,setUpdInpVal , getInp, add, delAll, removeItem, editItem , handleModalClose , handleModalOpen] = Todo();
return (
<View style={styles.container}>
<EditModal modalVisible = {modalVisible} updInpVal={updInpVal} setUpdInpVal = {setUpdInpVal} editItem = {editItem} indexNo = {indexNo}/>
<View>
<Heading />
<View>
<Input getInp={getInp} inpVal={inpVal} />
<Buttons add={add} delAll={delAll} />
</View>
<View>
<List todoArr={todoArr} handleModalOpen={handleModalOpen} removeItem={removeItem} />
</View>
</View>
</View >
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

Get the childrens of an element in react native using useRef

I am trying to understand how to use useRef in React Native and get the children of a View element, but I couldn't figure out to achieve it.
I am trying to use the .focus method in the TextInput component on press on the TouchableOpacity
Declaration:
const input = useRef(null);
TextInputComponent:
<View ref={input}>
<Tex>Email</Text>
<TextInput placeholder=""/>
</View>
Element:
<TouchableOpacity onPress={() => {}}>
<TextInputComponent />
</TouchableOpacity>
i tried input.current.children, but it returns undefined.
This is a working example of how to achieve what you want using on a basic expo init project on TypeScript:
App.tsx
import { StatusBar } from "expo-status-bar";
import { StyleSheet, Text, View, TextInput, TouchableOpacity} from "react-native";
import { useRef, forwardRef} from "react";
const TextInputComponent = forwardRef<TextInput>((props, ref) => {
return (
<View>
<Text>Email</Text>
<TextInput ref={ref} placeholder="Some placeholder" />
</View>
);
});
export default function App() {
const input = useRef<TextInput>(null)
return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
<StatusBar style="auto" />
<TouchableOpacity onPress={() => {
input.current?.focus();
}}>
<TextInputComponent ref={input} />
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
In this case it is more practical to use a forwardRef so you don't have to loop through the children of the View component to pass a ref from the component (App in this case) that is using TextInputComponent.

Error : You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports

I got this error when trying to import and use another component that I have created in another file. And I got an error like this :
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `Sidebar`.
This is my code to export component as default component in customDrawer.js which file is same directory:
const Sidebar = ({ isDarkTheme, setIsDarkTheme, ...props }) => {
const handlePress = () => {
props.navigation.dispatch(DrawerActions.closeDrawer());
props.navigation.navigate("Settings");
};
const { colors } = useTheme();
const toggleTheme = () => {
setIsDarkTheme();
};
// console.log('navi--?',props.navigation);
return (
<Container style={{ backgroundColor: colors.background }}>
<Header
style={{
backgroundColor: colors.background,
borderBottomWidth: 0,
marginTop: 70,
borderWidth: 0,
}}
>
<StatusBar barStyle="dark-content" backgroundColor="#ffffff" />
<Right>
<Button transparent>
<TouchableOpacity onPress={() => handlePress()}>
{/* <Ionicons name="ios-options" size={28} color={colors.icon} /> */}
</TouchableOpacity>
</Button>
</Right>
</Header>
<Content>
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
</DrawerContentScrollView>
<TouchableOpacity>
<View
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
paddingRight: 20,
paddingLeft: 20,
paddingTop: 10,
}}
>
<Text style={{ flex: 1, color: colors.text }}>Dark Theme</Text>
<View>
<Switch onValueChange={toggleTheme} value={isDarkTheme} />
</View>
</View>
</TouchableOpacity>
</Content>
</Container>
);
};
const styles = StyleSheet.create({});
export default Sidebar;
And then, in my App.js I have import components like this :
import Sidebar from "./customDrawer";
All imports I use in customDrawer.js :
import {
DrawerContentScrollView,
DrawerItemList,
} from "#react-navigation/drawer";
import { Button, Container, Content, Header, Right } from "native-base";
import React, { useState } from "react";
//import { Ionicons } from "#expo/vector-icons";
import { StyleSheet, Text, View, Switch } from "react-native";
import { StatusBar } from "react-native";
import { TouchableOpacity } from "react-native";
import { DrawerActions } from "#react-navigation/native";
import { useTheme } from "#react-navigation/native";

How to reuse a UI element in 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.

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>