How to fix "Not A Function" Error from React Native while declaring Dynamic Styles - react-native

I'm following a tutorial and I currently have this code where I am trying to change the color of a background based on a prop... This is what it looks like
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const SomeComponent = ({ bgColor }) => (
<View style={styles.wrapper(bgColor)}>
<Text style={styles.text}>3333</Text>
</View>
);
const styles = StyleSheet.create({
wrapper: color => ({
flex: 1,
backgroundColor: color,
}),
text: {
color: 'red',
},
});
I keep getting a styles.wrapper is not a function
How do I fix this?

You cannot declare a StyleSheet function like you did.
Alternatively, you can declare a JS function that passes the same style with different colors according to the arguments you pass.
You can do that as follows:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const myStyle = (color) => {
return {
flex: 1,
backgroundColor: color,
}
};
const SomeComponent = ({ bgColor }) => {
<View style={this.myStyle(bgColor)}>
<Text style={styles.text}>3333</Text>
</View>
};
const styles = StyleSheet.create({
text: {
color: 'red',
},
});

Related

Problem with lining up contents: react native

I'm currently having a problem with the clickable size of a reusable button which includes an icon and text. When I run this code it seems like the entire row becomes clickable when I only want the icon and text to become clickable. Does anyone know how to solve this problem? Thanks
App.js
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import IconTextButton from './components/iconTextButton';
export default function App() {
return (
<View style={styles.container}>
<Text style={{marginTop: 100}}>My First React App! Sike </Text>
<IconTextButton iconFont="ionicons" iconName="pencil" iconSize={25} text="Add Items"/>
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'powderblue',
},
});
iconTextButton.js
import React from 'react';
import { StyleSheet, TouchableOpacity, Text, View } from 'react-native';
import Ionicon from 'react-native-vector-icons/Ionicons';
export default function IconTextButton({ iconFont, iconName, iconSize, text, onPress }) {
const getIconFont = (iconFont) => {
switch (iconFont) {
case "ionicons":
return Ionicon;
}
};
const FontIcon = getIconFont(iconFont);
return (
<TouchableOpacity onPress={onPress} style={styles(iconSize).container>
<FontIcon name={iconName} size={iconSize} style={styles(iconSize).buttonIcon}>
<Text style={styles(iconSize).buttonText}>{text}</Text>
</FontIcon>
</TouchableOpacity>
)
}
const styles = (size) => StyleSheet.create({
container: {
backgroundColor: 'pink',
},
buttonIcon: {
backgroundColor: 'yellow',
width: size,
},
buttonText: {
backgroundColor: 'green'
},
})
Along with the code I've tried, I've also tried to keep and as seperate contents whilst adding a flexDirection: 'row' inside styles.container. This keeps the contents in the same line but it still makes the whole row clickable. I've also tried putting everything in a and moving the styles.container to the component and adding a height: size into styles.container. This makes the clickable component limited however, the component is hidden underneath due to the restricted height. I have also tried simply just using instead of making a reusable const that its an input. The same thing applies.
You can wrap your Icon and Text Component in a View component and then wrap it inside a TouchableOpacity Component
Try this or do something like this :
import React from 'react';
import { StyleSheet, TouchableOpacity, Text, View } from 'react-native';
import Ionicon from 'react-native-vector-icons/Ionicons';
export default function IconTextButton({ iconFont, iconName, iconSize, text, onPress }) {
const getIconFont = (iconFont) => {
switch (iconFont) {
case "ionicons":
return Ionicon;
}
};
const FontIcon = getIconFont(iconFont);
return (
<TouchableOpacity onPress={onPress} style={styles(iconSize).container}>
<View style={styles(iconSize).iconTextContainer}>
<FontIcon name={iconName} size={iconSize} style={styles(iconSize).buttonIcon} />
<Text style={styles(iconSize).buttonText}>{text}</Text>
</View>
</TouchableOpacity>
)
}
const styles = (size) => StyleSheet.create({
container: {
backgroundColor: 'pink',
},
iconTextContainer: {
flexDirection: 'row',
alignItems: 'center',
},
buttonIcon: {
backgroundColor: 'yellow',
width: size,
},
buttonText: {
backgroundColor: 'green'
},
})

React native StyleSheet.create where should I put

I need to pass props into Styles. So I created StyleSheet inside the class. But normal practice would be to create StyleSheet outside from the class.
I want to know are there any performance drawbacks by having StyleSheet.create inside the class ?
import React from 'react'
import { StyleSheet, View } from 'react-native'
import { connect } from 'react-redux'
import { Text } from 'native-base'
import p from '../../assets/colors/pallets'
const EmptyContainer = (props) => {
const texts = props.texts
const styles = StyleSheet.create({
emptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
backgroundColor: p.background2[props.theme],
},
text: {
color: p.text1[props.theme],
},
})
return (
<View style={styles.emptyContainer}>
{texts.map((text) => (
<Text style={styles.text} key={Math.random()}>
{text}
</Text>
))}
</View>
)
}
const mapStateToProps = (state) => {
const { theme } = state
return {
theme: theme.theme,
}
}
export default connect(mapStateToProps)(EmptyContainer)
Edited:
There are several ways to pass props into StyleSheet.
Having StyleSheet inside class itself.
Pass props as function parameter to styles i.e. styles(props.theme). emptyContainer
What would be the best way by considering the performance of the app ?

React native: can't configure the header with navigationOptions

I am new to react-native. I am trying to configure header styles for my app, but it's not working
App.js
import { StatusBar } from 'expo-status-bar';
import React, {useState} from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MealNavigator from './navigation/MealsNavigator';
export default function App() {
return (
<MealNavigator />
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
The following js file i am using for navigation
MealsNavigator.js
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from "react-navigation";
import categoriesScreen from '../screens/categoriesScreen';
import categoryMealScreen from '../screens/categoryMealScreen';
import mealDetailScreen from '../screens/mealDetailScreen';
const MealNavigator = createStackNavigator({
Categories : categoriesScreen,
CategoryMeals : categoryMealScreen,
MealDetail : mealDetailScreen
});
export default createAppContainer(MealNavigator);
The following is the screen where i am trying to configure the header
categoriesScreen.js
import React from 'react';
import {Text,View,Button,FlatList,StyleSheet,TouchableOpacity, Platform} from 'react-native';
import { CATEGORIES } from '../data/dummydata';
const CategoriesScreen = props => {
const renderGrid=(itemData) =>{
return(
<TouchableOpacity style={styles.gridItem} onPress={() =>{props.navigation.navigate({routeName:'CategoryMeals'});}}>
<View>
<Text>{itemData.item.title}</Text>
</View>
</TouchableOpacity>
)
};
return(
<View style={styles.container}>
<FlatList
data={CATEGORIES} renderItem={renderGrid} numColumns={2} />
</View>
);
}
CategoriesScreen.defaultNavigationOptions = ({ navigation }) =>({
title:'Meal Categories',
headerTitleStyle: {
textAlign: "left",
fontSize: 24
},
});
const styles = StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center'
},
gridItem:{
flexGrow:1,
padding: 20,
margin: 15,
height: 150,
}
});
export default CategoriesScreen;
The following is the dummy data i am using
dummydata.js
import Category from '../models/category';
export const CATEGORIES = [
new Category('c1','Indian','#f5428d'),
new Category('c2','Chinese','#f54242'),
new Category('c3','Thai','#f5a442'),
new Category('c4','Malaysian','#f5d142'),
new Category('c5','Arabian','#368dff'),
new Category('c6','South Indian','#41d95d'),
new Category('c7','Kerala','#9eecff'),
new Category('c8','Bengali','#b9ffb0'),
new Category('c9','Mexican','#ffc7ff'),
new Category('c10','Italian','#47fced'),
];
Following is category class file
category.js
class Category{
constructor(id,title,color){
this.id = id;
this.title = title;
this.color = color;
}
};
export default Category;
Everything else is working, just the header configuration is not working.I am using react navigation version 4
you can cofigure header using navigation.setOptions like this:
import React from 'react';
import {Text,View,Button,FlatList,StyleSheet,TouchableOpacity, Platform} from 'react-native';
import { CATEGORIES } from '../data/dummydata';
const CategoriesScreen = props => {
React.useLayoutEffect(() => {
props.navigation.setOptions({
headerTitle: 'Meal Categories',
headerTitleStyle: {
textAlign: "left",
fontSize: 24
},
});
},
const renderGrid=(itemData) =>{
return(
<TouchableOpacity style={styles.gridItem} onPress={() =>{props.navigation.navigate({routeName:'CategoryMeals'});}}>
<View>
<Text>{itemData.item.title}</Text>
</View>
</TouchableOpacity>
)
};
return(
<View style={styles.container}>
<FlatList
data={CATEGORIES} renderItem={renderGrid} numColumns={2} />
</View>
);
}
const styles = StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center'
},
gridItem:{
flexGrow:1,
padding: 20,
margin: 15,
height: 150,
}
});
export default CategoriesScreen;
I found a different solution to my problem. I used defaultNavigationOptions in my navigation file.
MealsNavigator.js
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from "react-navigation";
import categoriesScreen from '../screens/categoriesScreen';
import categoryMealScreen from '../screens/categoryMealScreen';
import mealDetailScreen from '../screens/mealDetailScreen';
import {Platform} from 'react-native';
import colors from '../constants/colors';
const MealNavigator = createStackNavigator({
Categories : {
screen : categoriesScreen,
navigationOptions : {
headerTitle:'Meal Categories',
}
},
CategoryMeals : {
screen : categoryMealScreen
},
MealDetail : mealDetailScreen
},
{
defaultNavigationOptions:{
headerTitleStyle:{
backgroundColor:Platform.OS==='android' ? colors.primaryColor : 'white'
},
headerTintColor:Platform.OS==='android' ? 'white' : colors.primaryColor
}
}
);
export default createAppContainer(MealNavigator);
Thanks to everybody for your help.

How can I get the text / value of a textinput by its ref?

In my parent I have this code:
So I render inside it my custom inputs by this way:
My doubt is how I can access on any part of this parent the text of each input using the ref. Someone can help me?
The textinput component:
https://gist.github.com/ThallyssonKlein/4e054bc368ebc153fbf9222e304ff887
I couldn't solve the problem, apparently there is no way to get this property in pure React-Native.
So I started using the TextInput component of the react-native-paper package. This way the same code worked, I can get the text now with this excerpt:
console.log(refContainerStep1.current.state.value);
use useRef() instead of createRef();
const textInput = useRef(null);
<TextInput
ref={textInput}
....../>
You can access the ref via refContainerStep1.current.
What you can then do is check the Prototype property to check which methods you can use.
I noticed there's a function called _getText which can be used to obtain a value.
An example of grabbing the value in an onPress:
const onPress = () => {
console.log(refContainerStep1.current.__proto__); // See available methods
console.log(refContainerStep1.current._getText()); // Grab the value
}
Do it that way
const onButtonClick = () => {
console.log('get value from parent')
console.log(ref1.current.props.value)
console.log(ref2.current.props.value)
};
Example in expo
Parent
import * as React from 'react';
import { Text, View, StyleSheet,TextInput } from 'react-native';
import Constants from 'expo-constants';
import MyTextInput from './components/AssetExample';
import { Card } from 'react-native-paper';
export default function App() {
const ref1 = React.createRef();
const ref2 = React.createRef();
const onButtonClick = () => {
console.log(ref1.current.props.value)
console.log(ref2.current.props.value)
};
return (
<View style={styles.container}>
<Card>
<button onClick={onButtonClick}>get value</button>
<MyTextInput label={'label 2'} secure={false} ref={ref1} />
<MyTextInput label={'label 1'} secure={true} ref={ref2} />
</Card>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Child
import React, { useState, useEffect } from 'react';
import { TextInput as RnTextInput, StyleSheet, View, Text } from 'react-native';
const styles = StyleSheet.create({
textInput: {
padding: 10,
marginRight: 10,
marginLeft: 10,
borderRadius: 50,
},
text: {
marginLeft: 20,
marginBottom: 10,
fontSize: 20,
},
});
const TextInput = React.forwardRef((props, ref) => {
const [text, setText] = useState('');
return (
<View>
{props.label && <Text style={styles.text}>{props.label}</Text>}
<RnTextInput
style={styles.textInput}
value={text}
onChange={(e) => {
setText(e.target.value);
}}
secureTextEntry={props.secure}
ref={ref}
/>
</View>
);
});
export default TextInput;

react-native useState error ("is read-only")

When running the code below in react-native, I get a "buttonColor is read-only" error.
I've been reviewing documentation from various places on useState but am still not sure what is causing this particular error.
I would expect for the button to alternate between 'green' (the initial state) and 'red' after each subsequent tap, but it just show green on the first render and then I get the error message after the first tap.
import React, {useState} from 'react';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
const ColorSquare = () => {
const[buttonColor, setColor] = useState('green');
const changeColor = () => {
if (buttonColor='green') {
setColor('red')
} else if (buttonColor='red') {
setColor('green')
} else {
setColor('blue')
}
}
return (
<View style={styles.container}>
<TouchableOpacity
style={{backgroundColor:buttonColor, padding: 15}}
onPress={()=>changeColor()}
>
<Text style={styles.text}>Change Color!</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
text:{
color:'white'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
export default ColorSquare;
Problem is you are assigning the value of button color instead of comparing it i.e using = instead of ===. You are basically setting a value to button color which is wrong.
import React, {useState} from 'react';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
const ColorSquare = () => {
const[buttonColor, setColor] = useState('green');
const changeColor = () => {
if (buttonColor==='green') { // use === instead of == //
setColor('red')
} else if (buttonColor==='red') { // use === instead of == //
setColor('green')
} else {
setColor('blue')
}
}
return (
<View style={styles.container}>
<TouchableOpacity
style={{backgroundColor:buttonColor, padding: 15}}
onPress={()=>changeColor()}
>
<Text style={styles.text}>Change Color!</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
text:{
color:'white'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
export default ColorSquare;
In addition to the answer posted by Atin above, I also thought of a way to do this using numerical values and an array of colors which I ultimately chose to go with due to needing some underlying binary representation of the data.
import React, {useState} from 'react';
import {StyleSheet, Text, View, TouchableOpacity} from 'react-native';
const ColorSquare = () => {
const[buttonNumber, setNumber] = useState(0);
const colors = ['green','red']
const changeColor = () => {
if (buttonNumber<1) {
setNumber(buttonNumber => buttonNumber + 1)
} else {
setNumber(buttonNumber => buttonNumber - 1)
}
}
return (
<View style={styles.container}>
<TouchableOpacity
style={{backgroundColor:colors[buttonNumber], padding: 15}}
onPress={()=>changeColor()}
>
<Text style={styles.text}>Change Color!</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
text:{
color:'white'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
});
export default ColorSquare;
HI there if you are in 2021 having this problem I want to show you something that I was doing wrong
In my case I was trying to use a handleChange function to setState look here below
const handleChange = (e) => {
setCustomerLogin({...customerLogin, [e.target.name]: e.target.value})
I was getting a setCustomerLogin is ready only and its because I wrapped the (e) in the above example.
Solution is
const handleChange = e => {
setCustomerLogin({...customerLogin, [e.target.name]: e.target.value})