This question already has answers here:
Passing Styles Based on Parent Component in React Native
(2 answers)
Closed 5 years ago.
I have a custom component called CardSection
import React from 'react';
import { View } from 'react-native';
const CardSection = (props) => {
return (
<View style={styles.containerStyle}>
{props.children}
</View>
);
};
const styles = {
containerStyle: {
borderBottomWidth: 1,
padding: 5,
backgroundColor: '#fff',
justifyContent: 'flex-start',
flexDirection: 'row',
borderColor: '#ddd',
position: 'relative'
}
};
export { CardSection };
When I instantiate this component from another class I would like to update one of the style elements while the others remain unchanged. The code below will only update the justifyContent element.
<CardSection style={{ justifyContent: 'space-between' }}>
The solution I have at the minute does not seem to be working and I would like to avoid duplicating the element with just a change to one of the style elements.
You could do the following:
//destruct props
const CardSection = ({ style, children }) => {
return (
// prop 'style' overrides standard containerStyle
<View style={[styles.containerStyle, style]}>
{children}
</View>
);
};
You can merge styles if you pass an array to styles:
const CardSection = (props) => {
return (
<View style={[styles.containerStyle, props.style]}>
{props.children}
</View>
);
};
They 'cascade' from left to right, meaning that latter styles in the array overwrite the former.
Here is the documentation for styling in react-native by default.
Related
This question already has answers here:
Firebase data not defined when used inside react useState
(2 answers)
Initiate React components with data from Firebase database?
(1 answer)
Closed 1 year ago.
I used the blow code to get data from the firebase:
import firebase from 'firebase';
import 'firebase/database'
firebase.database().ref().child('users').on('value',(snapshot)=>{
if(snapshot.exists()){
snapshot.forEach((datasnapshot)=>{
data.push({key: datasnapshot.key.toString()})
})
} else {
data.push({key: 'No one has written yet'})
}
});
var data = [];
export default data;
Later i tried to import the variable data to display it as below:
import React from 'react';
import {StyleSheet, Text, StatusBar, View, TouchableOpacity, ScrollView } from 'react-native';
import firebase from 'firebase';
import 'firebase/database'
import data from './getdata';
export default function Index({navigation}) {
return (
<View style={styles.container}>
<Text style={{color: '#000000', fontSize: 30, fontWeight: 'bold', marginTop: StatusBar.currentHeight}}>Index</Text>
<ScrollView>
{data.map((item,key)=>{
if(item.key == "No one has written yet"){return(<Text key={key} style={styles.item}>{item.key}</Text>)}
else{
return(
<TouchableOpacity key={key} onPress={()=>navigation.navigate('Details',item.key)}>
<Text style={styles.item}>{item.key}</Text>
</TouchableOpacity>
)
}
})}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
item: {
marginTop: 10,
fontSize: 20,
fontWeight: 'bold',
color: '#000000',
textAlign: 'center'
}
});
the problem is it does not show the data stored after the 'data' array is updated by 'push()' function until i save it once again and the code is refreshed in 'expo go' app.
Here is the image of how it is displayed at first when the app is opened:
Click for the image
I think the problem is that your component is not re-rendering after getting data thus the new data is not shown.
I would use Flatlist as it has extraData prop that will make Flatlist re-render when data set to it changes:
"By passing extraData={selected} to FlatList we make sure FlatList itself will re-render when the state changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is a PureComponent and the prop comparison will not show any changes."
https://docs.expo.dev/versions/latest/react-native/flatlist/
So changing your SrollView to...
<FlatList
data={data}
extraData={data}
renderItem={({ item, key }) => {
if(item.key == "No one has written yet"){
return(<Text key={key} style={styles.item}>{item.key}</Text>)
}else{
return(
<TouchableOpacity key={key} onPress={()=>navigation.navigate('Details',item.key)}>
<Text style={styles.item}>{item.key}</Text>
</TouchableOpacity>
)
}}
/>
I am trying to familiarize myself with React Native. At the moment I am working on an app but came across an issue when trying to display changes to the individual elements of an array. For example:
function MyApp() {
const [array, setArray] = useState([1,2,3]);
const onPress = () => {
let temp = [3,2,1];
setArray(temp);
}
return(
<View>
<TouchableHighlight onPress={onPress}>
<View>
<Text>{array[0]}</Text>
</View>
</TouchableHighlight>
</View>
);
}
With the above code, I expect '1' to be displayed in the text component and to change to '3' upon being pressed. console.log shows the state being changed but what is being displayed in the text component inside the actual app never updates. I then tried this using individual integer states like so:
const [int, setInt] = useState(0);
const onPress = () => {
setInt(1);
}
Using an integer state such as the one above works totally fine as expected. Can anyone show me what I am doing wrong with my array state? Thank you.
Your code looks perfect and should work without any issue.
Here is the slightly modified example where the first element is generated randomly and is being shown properly in the Text component.
Working Example: Expo Snack
import React, { useState } from 'react';
import { Text, View, StyleSheet, TouchableHighlight } from 'react-native';
import Constants from 'expo-constants';
export default function MyApp() {
const [array, setArray] = useState([1, 2, 3]);
const onPress = () => {
let temp = [Math.floor(Math.random() * 10), 2, 1];
setArray(temp);
};
return (
<View style={styles.container}>
<TouchableHighlight onPress={onPress}>
<View style={styles.btn}>
<Text
style={{ alignSelf: 'center', fontSize: 28, fontWeight: 'bold' }}>
{array[0]}
</Text>
</View>
</TouchableHighlight>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
btn: {
width: 100,
height: 100,
borderColor: 'purple',
borderWidth: 5,
justifyContent: 'center',
},
});
Your code looks to be fine, I tried the below following your code and can see that state and UI getting updated successfully when the button is being clicked.
Could you please check if your event handler function onPress is getting called, and if you are getting any error in the console when you click on it.
function App() {
const [array, setArray] = React.useState([1, 2, 3]);
const handleBtnClick = () => {
const temp = [3, 2, 1];
setArray(temp);
};
return (
<React.Fragment>
<button onClick={handleBtnClick}>Click</button>
{array.map((el) => (
<p>{el}</p>
))}
<hr />
{array[0]}
</React.Fragment>
);
}
I am trying to pass the fontSize as a style property to my component. The fontSize doesnt change any ideas how I can pass the fontSize as a prop to overwrite the default size?
<Homemenu navigation={navigation} style={styles.homeMenu} />
const styles = StyleSheet.create({
....
homeMenu: {
fontSize: 16,
},
});
In my homeMenu:
render() {
let {style, navigation} = this.props;
let fontSize = style && style.fontSize ? style.fontSize : null;
return (
<View style={styles.container}>
<View style={[styles.menuItem, fontSize]}>
....
</View>
const styles = StyleSheet.create({
...
menuItem: {
fontSize: 26
},
});
I guess you are unnecessary bother for falsy values in style prop. I don't see any issue of why it shouldn't work. Code looks good to me.
<Homemenu navigation={navigation} style={styles.homeMenu} />
const styles = StyleSheet.create({
...
homeMenu: {
fontSize: 16,
},
});
render() {
let { style, navigation } = this.props;
return (
<View style={styles.container}>
<View style={[styles.menuItem, style]}>
....
</View>
</View>
);
}
const styles = StyleSheet.create({
...
menuItem: {
fontSize: 26
},
});
You are doing it correct, the only change you need to make in
<View style={[styles.menuItem, fontSize]}>
is you should update the existing style object
style={{...styles.menuItem, fontSize}}
Basically it should be iterable, so that your second value can overrride the first one. And we should not enclose it with square brackets and we should use curly braces, since it is object.
is it possible to render a React.Component over other React.Component using just fat arrow function, using state seems unnecessary in my case as there is no need to close the opened Component. I am trying to achieve the simplest to render a React.Component over other React.Component.
I am trying to do it like this:
<Button onPress={() => { return (<ShowOtherReactComponent/>); }} >Show OtherComponent</Button>
this is calling the <ShowOtherReactComponent/> I know that because I called an alert function from constructor but! nothing is rendering. why is that? how can I do this?
PS: this approach may be wrong, but still wanna see how it can be done. for science.
You shouldn't return jsx from your handlers. Usually to show and or toggle components conditional rendering is the way to go.
Instead of returning <ShowOtherReactComponent/> from onPress you conditionally render the component based on a boolean binded to the local state and change the state instead.
const Component = () =>{
const [show, setShow] = useState(false)
const onPress = () => setShow(true)
return(
<>
<button onPress={onPress}> Show </button>
{ show && <ShowOtherReactComponent/> }
</>
)
}
I've made an example to show what you could potentially do if you wanted a button to add components to display:
import React from 'react';
import autoBind from 'react-autobind';
export default class ButtonTest extends React.Component {
constructor(props) {
super(props);
this.state = {
extraComponents : []
};
autoBind(this);
}
addComponent() {
const newComponent = (<p>I'm a new component</p>);
this.setState({extraComponents: [...this.state.extraComponents, newComponent]})
}
render() {
return (
<div>
<button onClick={this.addComponent}>add component</button>
{this.state.extraComponent}
</div>
)
}
}
I've checked it and it works.
import React, { useState } from 'react'
import { SafeAreaView, View, Text, Button, Dimensions } from 'react-native'
const App = () => {
const [visibilityOfOtherView, setvisibilityOfOtherView] = useState(false)
const { height, width } = Dimensions.get('window')
const SCREEN_HEIGHT = Math.round(height)
const SCREEN_WIDTH = Math.round(width)
return (
<SafeAreaView style={{ height: SCREEN_HEIGHT, width: SCREEN_WIDTH, }}>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'red' }}>
<Text style={{ marginBottom: 20 }}>
First Components
</Text>
<Button
title='Toggle Components View'
onPress={() => setvisibilityOfOtherView(!visibilityOfOtherView)}
/>
</View>
{
visibilityOfOtherView ?
<View style={{ height: SCREEN_HEIGHT, width: SCREEN_WIDTH, alignItems: 'center', justifyContent: 'center', backgroundColor: 'green' }}>
<Text style={{ marginBottom: 20 }}>
Secound Components
</Text>
<Button
title='Toggle Components View'
onPress={() => setvisibilityOfOtherView(!visibilityOfOtherView)}
/>
</View>
: null
}
</SafeAreaView>
)
}
export default App
I'm trying to understand the best practice for naming component styles in react-native. I understand that style-sheets are scoped at component level but I want to understand if i have two different components whether its bad to use the same css class name for different styles in different components. For example:
component one:
render() {
return (
<View style={styles.container}>
</View>
)
}
const componentOneStyles = StyleSheet.create({
container: {
backgroundColor: 'red'
}
});
component two:
render() {
return (
<View style={styles.container}>
</View>
)
}
const componentTwoStyles = StyleSheet.create({
container: {
backgroundColor: 'green'
}
});
Given this example is it ok to use container for both these components or would it be better practice to use containerOne and containerTwo.