Related
I was recently working on a React Native project and I choose to use KeyBoardAwareScrollView to handle the keyboard scroll of the screen. The Below image shows the design which should be actual but when I use keyboardAvoidingView it starts to add padding to the bottom of the screen while I want the screen to scroll up when the keyboard is visible, after doing some research I came across the KeyboardAvoidingScrollView but using that if i give they height of
flex: 1
If i change this line to a static height it fixes the issue on a static screen
scrollContainer: { height: 1000 }
to the container that holds the content everything is pushed to the top.
To somehow adjust the view I gave the scroll container fixed height that did solve the problem temporarily but didn't work on all screen sizes.
My Code Looks like this
import {
View,
Text,
StyleSheet,
SafeAreaView,
Image,
StatusBar,
KeyboardAvoidingView,
} from "react-native";
import AuthContent from "../../auth/AuthContent";
import AuthForm from "../../auth/AuthForm";
import CustomTextInput from "../../components/CustomTextInput";
import { setEmailPass } from "../../../redux/UserReucer";
import { useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { ImageBackground } from "react-native";
import { GlobalStyles } from "../../../consts/GlobalConsts";
import OrientationLoadingOverlay from "react-native-orientation-loading-overlay";
import { useState } from "react";
import Loading from "../../components/Loading";
import { getAxiosClient } from "../../apis/TallyApi";
import { showAlert } from "../../../utils/Alert";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
const SignUpScreen = ({ navigation }) => {
const dispatch = useDispatch();
const currentUser = useSelector((state) => {
return state.mUser;
});
const [isLoading, setIsLoading] = useState(false);
// console.log("Redux state", currentUser);
async function loginHandler({ email, password }) {
setIsLoading(true);
console.log("Signup Screen", email, password);
try {
let response = await getAxiosClient().post("/users/checkEmail", {
email: email,
});
dispatch(setEmailPass({ email, password }));
navigation.navigate("RoleScreen");
console.log("Axios response ", response.data);
} catch (err) {
// const { response } = err;
// const { request, ...errorObject } = response;
console.log("Axios Error ", err.response.data.message);
showAlert(err.response.data.message);
// console.error(err);
// console.error(err);
} finally {
setIsLoading(false);
}
}
return (
<>
<ImageBackground
source={require("../../../../assets/background_color.png")}
style={GlobalStyles.backgroundContainer}
>
<SafeAreaView style={styles.container}>
<KeyboardAwareScrollView
style={styles.container}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<View style={styles.scrollContainer}>
<StatusBar barStyle="light-content" />
<View style={styles.topSpace}></View>
<View style={styles.mainContainer}>
<View style={styles.imageContainer}>
<Image
style={styles.image}
source={require("../../../../assets/email_icn.png")}
></Image>
</View>
<View style={styles.textContainer}>
<Text style={styles.largeText}>Continue with email</Text>
<Text style={styles.smallText}>
Enter your email address to get register with palace
</Text>
</View>
<View style={styles.authContainer}>
<AuthContent
styles={styles.authContainer}
onAuthenticate={loginHandler}
></AuthContent>
</View>
</View>
<View style={styles.bottomSpace}></View>
<Loading isLoading={isLoading}></Loading>
</View>
</KeyboardAwareScrollView>
</SafeAreaView>
</ImageBackground>
</>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
scrollContainer: { flex: 1 },
largeText: {
fontSize: 30,
color: "white",
marginVertical: 9,
textAlign: "center",
},
smallText: {
fontSize: 16,
color: "white",
marginVertical: 9,
textAlign: "center",
},
topSpace: {
flex: 0.5,
},
mainContainer: {
flex: 8.5,
},
bottomSpace: {
flex: 1,
},
imageContainer: {
flex: 3,
alignItems: "center",
justifyContent: "center",
},
textContainer: {
flex: 1.5,
justifyContent: "center",
},
authContainer: {
flex: 2.5,
},
image: {
resizeMode: "contain",
height: "90%",
},
});
export default SignUpScreen;
Just use a <ScrollView> rather than <KeyboardAwareScrollView>
This is the error I get-->
I have a HomeFeed.js where I list all my articles and when you click on each article you get taken to the Content.js where you can see the article details/content.
In the HomeFeed.js, when you press on the article this happens-->
..........................................................................................................................................................................................................................................................................................................................................................................................
onPress={() => {
this.props.navigation.navigate("Content", {
articlekey: `${JSON.stringify(item.key)}`,
});
}}
..........................................................................................................................................................................................................................................................................................................................................................................................
In my Content.js, I have the below:
import React, { Component } from "react";
import { StyleSheet, ScrollView, ActivityIndicator, View } from "react-native";
import { Text, Card } from "react-native-elements";
import firebase from "../../firebase";
class Content extends Component {
static navigationOptions = {
title: "Content",
};
constructor() {
super();
this.state = {
isLoading: true,
article: {},
key: "",
};
}
componentDidMount() {
const { navigation } = this.props;
const ref = firebase
.firestore()
.collection("articles")
.doc(JSON.parse(navigation.getParam("articlekey")));
ref.get().then((doc) => {
if (doc.exists) {
this.setState({
article: doc.data(),
key: doc.id,
isLoading: false,
});
} else {
console.log("No such document!");
}
});
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.activity}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
}
return (
<ScrollView>
<Card style={styles.container}>
<View style={styles.subContainer}>
<View>
<Text h3>{this.state.article.title}</Text>
</View>
<View>
<Text h5>{this.state.article.content}</Text>
</View>
</View>
</Card>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
},
subContainer: {
flex: 1,
paddingBottom: 20,
borderBottomWidth: 2,
borderBottomColor: "#CCCCCC",
},
activity: {
position: "absolute",
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: "center",
justifyContent: "center",
},
detailButton: {
marginTop: 10,
},
});
export default Content;
The problem is with your get method of parameters. With react-navigation version 5.X or upper, you need to get params with route.
SOLUTION
Change componentDidMount( ) like this
componentDidMount() {
// const { navigation } = this.props; remove this
const { articlekey } = this.props.route.params; //add this
const ref = firebase
.firestore()
.collection("articles")
.doc(JSON.parse(articlekey));
ref.get().then((doc) => {
if (doc.exists) {
this.setState({
article: doc.data(),
key: doc.id,
isLoading: false,
});
} else {
console.log("No such document!");
}
});
}
I'm making an app that makes a bill of items and the user can identify the quantity of every item
and at least there is a text tag which it shows the total price after hitting " get bill " button
it works fine , but the issue is after hitting the button the items are repeated again
[here a screenshot of the app after hitting a 'get bill' button]: https://i.stack.imgur.com/slOWN.jpg
this is the code :
import React, { Component } from "react";
import { View, FlatList, Dimensions, Text } from "react-native";
import * as SQLite from "expo-sqlite";
import { Button } from "react-native-elements";
import NumericInput from "react-native-numeric-input";
const db = SQLite.openDatabase("db.db");
class CreateBill extends Component {
constructor() {
super();
this.state = {
items: [],
itemsList: [],
products: [],
totalPrice: 0,
};
}
async componentDidMount() {
console.log("did mount");
db.transaction((tx) => {
tx.executeSql("SELECT * FROM products2", [], (tx, results) => {
var temp = [];
for (let i = 0; i < results.rows.length; ++i) {
temp.push(results.rows.item(i));
}
this.setState({
products: temp,
});
});
});
}
checkItems = () => {
this.state.items.forEach((item) => {
this.state.products.forEach((product) => {
if (item === product.code) {
console.log("equals", item, product.code);
this.state.itemsList.push({ product, number: 0 });
} else {
console.log("doesnt equal", item.item, product.code);
}
});
});
};
getPrice = () => {
let price = 0;
this.state.itemsList.forEach((element) => {
price = element.number * element.product.price;
});
this.setState({ totalPrice: price });
};
render() {
this.state.items = this.props.route.params.items;
this.checkItems();
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.itemsList}
extraData={this.state}
style={{ marginTop: 50, flex: 1, width: Dimensions.width }}
keyExtractor={(item) => item.product.id}
renderSeparator={() => (
<View
style={{
backgroundColor: "#1B73B4",
height: 0.6,
}}
/>
)}
renderItem={({ item, index }) => {
console.log("item", item);
return (
<View
style={{
flexDirection: "row",
width: Dimensions.width,
paddingStart: 20,
alignItems: "center",
}}
key={item}
>
<View style={{ flex: 0.5, alignItems: "center" }}>
<Text style={{ fontSize: 24 }}>{item.product.name}</Text>
</View>
<View style={{ flex: 0.5, justifyContent: "center" }}>
<NumericInput
value={this.state.itemsList[index].number}
onChange={(value) => {
let item1 = this.state.itemsList[index];
item1.number = value;
console.log(
this.state.itemsList[index].product.name,
this.state.itemsList[index].number
);
}}
totalWidth={140}
totalHeight={45}
iconSize={25}
maxValue={this.state.itemsList[index].product.units}
minValue={0}
valueType="integer"
rounded
textColor="#1B73B4"
iconStyle={{ color: "white" }}
rightButtonBackgroundColor="#1B73B4"
leftButtonBackgroundColor="#1B73B4"
/>
</View>
</View>
);
}}
/>
<Text>{this.state.totalPrice}</Text>
<Button title="get bill" onPress={() => this.getPrice()} />
</View>
);
}
}
export { CreateBill };
In your case reduce is the best option:
Documentation here
Example:
const totalPrice = this.state.itemsList.reduce((total, element) =>
total += element.number * element.product.price
,0);
this.setState({ totalPrice });
Trying out react navigation v5 so I created a login register form that brings the user to the dashboard if he's logged in before using AsyncStorage. It works fine but I would like to know how to improve on the structure of code. Currently, I put all the components in a stack navigator, but things might get messy if I were to add more screens. Also, I would be switching to hooks but am confused if and how I should apply useContext useMemo useEffect
App.js file
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import Login from './components/login';
import Signup from './components/signup';
import Dashboard from './components/dashboard';
import AuthLoading from './components/authLoading'
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator
initialRouteName="AuthLoading"
screenOptions={{
headerTitleAlign: 'center',
headerStyle: {
backgroundColor: '#3740FE',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}>
<Stack.Screen
name="AuthLoading"
component={AuthLoading}
options={
{title: 'AuthLoading'},
{headerLeft:null}
}/>
<Stack.Screen
name="Signup"
component={Signup}
options={{ title: 'Signup' }}
/>
<Stack.Screen
name="Login"
component={Login}
options={
{ title: 'Login' },
{ headerLeft: null }
}
/>
<Stack.Screen
name="Dashboard"
component={Dashboard}
options={
{ title: 'Dashboard' },
{ headerLeft: null }
}
/>
</Stack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}
authLoading.js file
import React from 'react';
import { ActivityIndicator, StatusBar, StyleSheet, View, Text } from 'react-native';
import {AsyncStorage} from 'react-native'
export default class AuthLoadingScreen extends React.Component {
constructor(props) {
super(props);
this.checkUserToken();
}
async checkUserToken() {
const isLoggedIn = await AsyncStorage.getItem('isLoggedIn');
console.log(isLoggedIn)
// If User Token
if (isLoggedIn === '1') {
//AsyncStorage.setItem(isLoggedIn);
this.props.navigation.navigate('Dashboard');
}
else {
this.props.navigation.navigate('Login');
}
}
// Render any loading content that you like here
render() {
return (
<View style={styles.container}>
<Text style={styles.text}>Checking Authentication</Text>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
// Styles
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#CA3433',
justifyContent: 'center',
alignItems: 'center',
},
text: {
justifyContent: 'center',
color: '#fff',
fontSize: 18,
fontWeight: '500',
},
});
signup.js file
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button, Alert, ActivityIndicator } from 'react-native';
import firebase from '../database/firebase';
export default class Signup extends Component {
constructor() {
super();
this.state = {
displayName: '',
email: '',
password: '',
isLoading: false
}
}
updateInputVal = (val, prop) => {
const state = this.state;
state[prop] = val;
this.setState(state);
}
registerUser = () => {
if (this.state.email === '' && this.state.password === '') {
Alert.alert('Enter details to signup!')
} else {
this.setState({
isLoading: true,
})
firebase
.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password)
.then((res) => {
res.user.updateProfile({
displayName: this.state.displayName
})
console.log('User registered successfully!')
this.setState({
isLoading: false,
displayName: '',
email: '',
password: ''
})
this.props.navigation.navigate('Login')
})
.catch(error => this.setState({
isLoading: false,
errorMessage: error.message
}))
}
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.preloader}>
<ActivityIndicator size="large" color="#9E9E9E" />
</View>
)
}
return (
<View style={styles.container}>
<TextInput
style={styles.inputStyle}
placeholder="Name"
value={this.state.displayName}
onChangeText={(val) => this.updateInputVal(val, 'displayName')}
/>
<TextInput
style={styles.inputStyle}
placeholder="Email"
value={this.state.email}
onChangeText={(val) => this.updateInputVal(val, 'email')}
/>
<TextInput
style={styles.inputStyle}
placeholder="Password"
value={this.state.password}
onChangeText={(val) => this.updateInputVal(val, 'password')}
maxLength={15}
secureTextEntry={true}
/>
<Button
color="#3740FE"
title="Signup"
onPress={() => this.registerUser()}
/>
<Text>{this.state.errorMessage}</Text>
<Text
style={styles.loginText}
onPress={() => this.props.navigation.navigate('Login')}>
Already Registered? Click here to login
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
padding: 35,
backgroundColor: '#fff'
},
inputStyle: {
width: '100%',
marginBottom: 15,
paddingBottom: 15,
alignSelf: "center",
borderColor: "#ccc",
borderBottomWidth: 1
},
loginText: {
color: '#3740FE',
marginTop: 25,
textAlign: 'center'
},
preloader: {
left: 0,
right: 0,
top: 0,
bottom: 0,
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#fff'
}
});
login.js file
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button, Alert, ActivityIndicator } from 'react-native';
import firebase from '../database/firebase';
import {AsyncStorage} from 'react-native';
export default class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
isLoading: false,
errorMessage: ''
}
}
updateInputVal = (val, prop) => {
const state = this.state;
state[prop] = val;
this.setState(state);
}
/**
*
*/
userLogin = () => {
if (this.state.email === '' && this.state.password === '') {
this.setState({
errorMessage: "Enter details to sign in"
})
//Alert.alert('Enter details to signin!')
} else {
this.setState({
isLoading: true,
})
firebase
.auth()
.signInWithEmailAndPassword(this.state.email, this.state.password)
.then((res) => {
console.log(res)
console.log('User logged-in successfully!')
this.setState({
isLoading: false,
email: '',
password: ''
})
AsyncStorage.setItem('isLoggedIn', '1')
this.props.navigation.navigate('Dashboard')
})
.catch(error => this.setState({
errorMessage: 'Wrong email/password',
isLoading: false
}))
}
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.preloader}>
<ActivityIndicator size="large" color="#9E9E9E" />
</View>
)
}
return (
<View style={styles.container}>
<TextInput
style={styles.inputStyle}
placeholder="Email"
value={this.state.email}
onChangeText={(val) => this.updateInputVal(val, 'email')}
/>
<TextInput
style={styles.inputStyle}
placeholder="Password"
value={this.state.password}
onChangeText={(val) => this.updateInputVal(val, 'password')}
maxLength={15}
secureTextEntry={true}
/>
<Button
color="#3740FE"
title="Signin"
onPress={() => this.userLogin()}
/>
{/* style this */}
<Text>{this.state.errorMessage}</Text>
<Text
style={styles.loginText}
onPress={() => this.props.navigation.navigate('Signup')}>
Don't have account? Click here to signup
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
padding: 35,
backgroundColor: '#fff'
},
inputStyle: {
width: '100%',
marginBottom: 15,
paddingBottom: 15,
alignSelf: "center",
borderColor: "#ccc",
borderBottomWidth: 1
},
loginText: {
color: '#3740FE',
marginTop: 25,
textAlign: 'center'
},
preloader: {
left: 0,
right: 0,
top: 0,
bottom: 0,
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#fff'
}
});
dashboard.js file
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput, Button, Alert, ActivityIndicator } from 'react-native';
import firebase from '../database/firebase';
import {AsyncStorage} from 'react-native';
export default class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
isLoading: false,
errorMessage: ''
}
}
updateInputVal = (val, prop) => {
const state = this.state;
state[prop] = val;
this.setState(state);
}
/**
*
*/
userLogin = () => {
if (this.state.email === '' && this.state.password === '') {
this.setState({
errorMessage: "Enter details to sign in"
})
//Alert.alert('Enter details to signin!')
} else {
this.setState({
isLoading: true,
})
firebase
.auth()
.signInWithEmailAndPassword(this.state.email, this.state.password)
.then((res) => {
console.log(res)
console.log('User logged-in successfully!')
this.setState({
isLoading: false,
email: '',
password: ''
})
AsyncStorage.setItem('isLoggedIn', '1')
this.props.navigation.navigate('Dashboard')
})
.catch(error => this.setState({
errorMessage: 'Wrong email/password',
isLoading: false
}))
}
}
render() {
if (this.state.isLoading) {
return (
<View style={styles.preloader}>
<ActivityIndicator size="large" color="#9E9E9E" />
</View>
)
}
return (
<View style={styles.container}>
<TextInput
style={styles.inputStyle}
placeholder="Email"
value={this.state.email}
onChangeText={(val) => this.updateInputVal(val, 'email')}
/>
<TextInput
style={styles.inputStyle}
placeholder="Password"
value={this.state.password}
onChangeText={(val) => this.updateInputVal(val, 'password')}
maxLength={15}
secureTextEntry={true}
/>
<Button
color="#3740FE"
title="Signin"
onPress={() => this.userLogin()}
/>
{/* style this */}
<Text>{this.state.errorMessage}</Text>
<Text
style={styles.loginText}
onPress={() => this.props.navigation.navigate('Signup')}>
Don't have account? Click here to signup
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
display: "flex",
flexDirection: "column",
justifyContent: "center",
padding: 35,
backgroundColor: '#fff'
},
inputStyle: {
width: '100%',
marginBottom: 15,
paddingBottom: 15,
alignSelf: "center",
borderColor: "#ccc",
borderBottomWidth: 1
},
loginText: {
color: '#3740FE',
marginTop: 25,
textAlign: 'center'
},
preloader: {
left: 0,
right: 0,
top: 0,
bottom: 0,
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#fff'
}
});
React-Navigation provided help regarding the auth flow on their official site. Just follow the below link
https://reactnavigation.org/docs/auth-flow/
Note: You can follow this article for authentication flow using hooks and react-navigation 5
https://dev.to/embeddednature/create-an-authorization-flow-with-react-navigation-5-x-2pkh
Say I have a component with a render like this:
<View style={jewelStyle}></View>
Where jewelStyle =
{
borderRadius: 10,
backgroundColor: '#FFEFCC',
width: 20,
height: 20,
},
How could I make the background colour dynamic and randomly assigned? I've tried
{
borderRadius: 10,
backgroundColor: getRandomColor(),
width: 20,
height: 20,
},
But this makes all instances of View have the same colour, I want each one to be unique.
Any tips?
I usually do something along the lines of:
<View style={this.jewelStyle()} />
...
jewelStyle = function(options) {
return {
borderRadius: 12,
background: randomColor(),
}
}
Every time View is rendered, a new style object will be instantiated with a random color associated with it. Of course, this means that the colors will change every time the component is re-rendered, which is perhaps not what you want. Instead, you could do something like this:
var myColor = randomColor()
<View style={jewelStyle(myColor)} />
...
jewelStyle = function(myColor) {
return {
borderRadius: 10,
background: myColor,
}
}
Yes you can and actually, you should use StyleSheet.create to create your styles.
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View
} from 'react-native';
class Header extends Component {
constructor(props){
super(props);
}
render() {
const { title, style } = this.props;
const { header, text } = defaultStyle;
const combineStyles = StyleSheet.flatten([header, style]);
return (
<View style={ combineStyles }>
<Text style={ text }>
{ title }
</Text>
</View>
);
}
}
const defaultStyle = StyleSheet.create({
header: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
height: 60,
paddingTop: 15,
shadowColor: '#000',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.4,
elevation: 2,
position: 'relative'
},
text: {
color: '#0d4220',
fontSize: 16
}
});
export default Header;
And then:
<Header title="HOME" style={ {backgroundColor: '#10f1f0'} } />
If you still want to take advantage of StyleSheet.create and also have dynamic styles, try this out:
const Circle = ({initial}) => {
const initial = user.pending ? user.email[0] : user.firstName[0];
const colorStyles = {
backgroundColor: randomColor()
};
return (
<View style={[styles.circle, colorStyles]}>
<Text style={styles.text}>{initial.toUpperCase()}</Text>
</View>
);
};
const styles = StyleSheet.create({
circle: {
height: 40,
width: 40,
borderRadius: 30,
overflow: 'hidden'
},
text: {
fontSize: 12,
lineHeight: 40,
color: '#fff',
textAlign: 'center'
}
});
Notice how the style property of the View is set as an array that combines your stylesheet with your dynamic styles.
The easiest is mine:
<TextInput
style={[
styles.default,
this.props.singleSourceOfTruth ?
{ backgroundColor: 'black' }
: { backgroundColor: 'white' }
]}/>
Had some issue syntactically.
This worked for me
<Text style={[styles.textStyle,{color: 'red'}]}> Hello </Text>
const styles = StyleSheet.create({
textStyle :{
textAlign: 'center',
fontFamily: 'Arial',
fontSize: 16
}
});
You'll want something like this:
var RandomBgApp = React.createClass({
render: function() {
var getRandomColor = function() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
var rows = [
{ name: 'row 1'},
{ name: 'row 2'},
{ name: 'row 3'}
];
var rowNodes = rows.map(function(row) {
return <Text style={{backgroundColor:getRandomColor()}}>{row.name}</Text>
});
return (
<View>
{rowNodes}
</View>
);
}
});
In this example I take the rows array, containing the data for the rows in the component, and map it into an array of Text components. I use inline styles to call the getRandomColor function every time I create a new Text component.
The issue with your code is that you define the style once and therefore getRandomColor only gets called once - when you define the style.
I know this is extremely late, but for anyone still wondering here's an easy solution.
You could just make an array for the styles :
this.state ={
color: "#fff"
}
style={[
styles.jewelstyle, {
backgroundColor: this.state.BGcolor
}
The second will override any original background color as stated in the stylesheet. Then have a function that changes the color:
generateNewColor(){
var randomColor = '#'+Math.floor(Math.random()*16777215).toString(16);
this.setState({BGcolor: randomColor})
}
This will generate a random hex color. Then just call that function whenever and bam, new background color.
Actually, you can write your StyleSheet.create object as a key with function value, it works properly but it has a type issue in TypeScript:
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',
},
});
import React, { useContext, useMemo } from 'react';
import { Text, StyleSheet, View } from 'react-native';
import colors from '../utils/colors';
import ThemeContext from './../contexts/ThemeContext';
export default (props) => {
const { theme } = useContext(ThemeContext);
// Constructing styles for current theme
const styles = useMemo(() => createStyles(theme), [theme]);
return (
<View style={styles.container}>
<Text style={styles.label}>{label}</Text>
</View>
);
};
const createStyles = (theme: AppTheme) =>
StyleSheet.create({
container: { width: '100%', position: 'relative', backgroundColor: colors[theme].background },
label: {
fontSize: 13,
fontWeight: 'bold',
},
});
colors.ts
export type AppTheme = 'dark' | 'light';
const light: Colors = {
background: '#FFFFFF',
onBackground: '#333333',
gray: '#999999',
grayLight: '#DDDDDD',
red: 'red',
};
const dark: Colors = {
background: '#333333',
onBackground: '#EEEEEE',
gray: '#999999',
grayLight: '#DDDDDD',
red: 'red',
};
const colors = {
dark,
light,
primary: '#2E9767',
secondary: '#F6D130',
};
export default colors;
Using object spread operator "..." worked for me:
<View style={{...jewelStyle, ...{'backgroundColor': getRandomColor()}}}></View>
Yes, you can make dynamic styles. You can pass values from Components.
First create StyleSheetFactory.js
import { StyleSheet } from "react-native";
export default class StyleSheetFactory {
static getSheet(backColor) {
return StyleSheet.create({
jewelStyle: {
borderRadius: 10,
backgroundColor: backColor,
width: 20,
height: 20,
}
})
}
}
then use it in your component following way
import React from "react";
import { View } from "react-native";
import StyleSheetFactory from './StyleSheetFactory'
class Main extends React.Component {
getRandomColor = () => {
var letters = "0123456789ABCDEF";
var color = "#";
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
render() {
return (
<View>
<View
style={StyleSheetFactory.getSheet(this.getRandomColor()).jewelStyle}
/>
<View
style={StyleSheetFactory.getSheet(this.getRandomColor()).jewelStyle}
/>
<View
style={StyleSheetFactory.getSheet(this.getRandomColor()).jewelStyle}
/>
</View>
);
}
}
<View
style={[styles.categoryItem,{marginTop: index <= numOfColumns-1 ? 10 : 0 }]}
>
I know there are several answers, but i think the best and most simple is using a state "To change" is the state purpose.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
style: {
backgroundColor: "white"
}
};
}
onPress = function() {
this.setState({style: {backgroundColor: "red"}});
}
render() {
return (
...
<View style={this.state.style}></View>
...
)
}
}
You can bind state value directly to style object. Here is an example:
class Timer extends Component{
constructor(props){
super(props);
this.state = {timer: 0, color: '#FF0000'};
setInterval(() => {
this.setState({timer: this.state.timer + 1, color: this.state.timer % 2 == 0 ? '#FF0000' : '#0000FF'});
}, 1000);
}
render(){
return (
<View>
<Text>Timer:</Text>
<Text style={{backgroundColor: this.state.color}}>{this.state.timer}</Text>
</View>
);
}
}
If you are using a screen with filters for example, and you want to set the background of the filter regarding if it was selected or not, you can do:
<TouchableOpacity style={this.props.venueFilters.includes('Bar')?styles.filterBtnActive:styles.filterBtn} onPress={()=>this.setFilter('Bar')}>
<Text numberOfLines={1}>
Bar
</Text>
</TouchableOpacity>
On which set filter is:
setVenueFilter(filter){
var filters = this.props.venueFilters;
filters.push(filter);
console.log(filters.includes('Bar'), "Inclui Bar");
this.setState(previousState => {
return { updateFilter: !previousState.updateFilter };
});
this.props.setVenueFilter(filters);
}
PS: the function this.props.setVenueFilter(filters) is a redux action, and this.props.venueFilters is a redux state.
You can do something like this.
In your component:
const getRandomColor = () => {
// you can use your component props here.
}
<View style={[styles.jewelStyle, {backgroundColor: getRandomColor()}]} />
Create your style using stylesheet:
const styles = StyleSheet.create({
jewelStyle: {
backgroundColor: 'red',
},
});
If you are following the functional approach of React-Native, you can use a package called dynamic-styles that tries to solve exactly your problem.
// -- theme.js ------------------------------------------------------
// Initialization of a StyleSheet instance called 'styleSheet'
export const styleSheet = createStyleSheet({
theme: /* optional theme */
});
// -- MyComponent.js -----------------------------------------------
// Create dynamic stylesheet that has access
// to the previously specified theme and parameters
const useStyles = styleSheet.create(({theme, params}) => ({
root: /* Dynamic Styles */,
button: /* Dynamic Styles */,
text: /* Dynamic Styles */,
}));
const MyComponent = (props) => {
// Access dynamic styles using the created 'useStyles()' hook
// and specify the corresponding parameters
const { styles } = useStyles({ color: props.color, fontSize: 10 });
return (
<div className={styles.root}>
{/* */}
</div>
);
}
It basically allows you to create dynamic stylesheets
and link them to functional Components using the React hook pattern.
-> Codesandbox
In case someone needs to apply conditions
selectedMenuUI = function(value) {
if(value==this.state.selectedMenu){
return {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
paddingVertical: 10,
backgroundColor: 'rgba(255,255,255,0.3)',
borderRadius: 5
}
}
return {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
paddingVertical: 10
}
}
Here is what worked for me:
render() {
const { styleValue } = this.props;
const dynamicStyleUpdatedFromProps = {
height: styleValue,
width: styleValue,
borderRadius: styleValue,
}
return (
<View style={{ ...styles.staticStyleCreatedFromStyleSheet, ...dynamicStyleUpdatedFromProps }} />
);
}
For some reason, this was the only way that mine would update properly.
you can use styled-components for react native it will provide you dynamic styling just like emotion or styled-components for web.
For something relatively simple, you can use this approach:
StyleSheet.create({
item: props.selectedId === item.id ? {
backgroundColor: 'red',
}: null
});