Bad practise to generate new StyleSheet every render? - reactjs

Every documentation example that I have read about creating a StyleSheet for a component has done it outside of render() (even like a regular variable outside the component class). Doing it that way means I have no control over props or state changes that can manipulate the style of said component. As such I have been calling a getStyles() function inside the render() method which creates a (new) StyleSheet on every render. To me it sounds expensive performance-wise, but it does the job. However, I’m wondering if there is a better way of doing it?
Thanks in advance!

Yes! Accourding to the StyleSheet Docs it is not the best idea to create a StyleSheet during every render because of performance and code-readability.
There is a better way of doing it by using array notations, basically meaning you can pass in an array of style objects
for instance if I had a component that has it's background color and text color set in a styles object like this:
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
export default class SomeComponent extends Component{
render(){
return(
<View style={styles.root}>
<Text>{'Hi Everybody!'}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
root: {
backgroundColor: '#000000',
height: 400,
width: 300,
color: 'white'
}
});
You can customize it the background color(or any other style) like so:
<View style={[styles.root, {backgroundColor: "yellow"}]}>
<Text>{'Hi Everybody!'}</Text>
</View>
In your case, you might pass in a props value like so :
<View style={[styles.root, this.props.style]}>
<Text>{'Hi Everybody!'}</Text>
</View>
or a variable from the state object like so :
<View style={[styles.root, this.state.style]}>
<Text>{'Hi Everybody!'}</Text>
</View>
The later values in the array will take precedence, so if I added yet another style object that has a backgroundColor attribute, the last backgroundColor will be applied;
For example if I did this:
<View style={[styles.root, {backgroundColor: "yellow"}, {backgroundColor: "green"}]}>
<Text>{'Hi Everybody!'}</Text>
</View>
the background color will be green.
So you can now write your code in such a way that the styles created with StyleSheet.create({}) will contain the boilerplate styles that are constant and apply to any customization, or just the default styles.
Hope that helps.

Related

Set text color of React Native WebView

I have a string of HTML which is to be displayed in a WebView. How do I set the text color of a React Native WebView?
<WebView source={{ html: this.props.content }}/>
To change the color of the text inside a HTML, wrap the html in a div tag and set the font color of the div in a style.
html = '<div style="color: white">' + html + '</div>';
<WebView source={{ html: html }} />
If there is a small amount of content (e.g., as a test), you could insert the html string directly with formatting e.g.
<WebView source={{ html: "<strong>This is my Webview</strong>" }} />
The other way would be import your styles into your code and then export the lot as a component:
import React, { Component } from 'react'
import {WebView} from 'react-native'
class ExternalWidget extends Component {
render()
{
<WebView source={{uri:'./external/widget/index.html'}}
scalesPageToFit/>
}
}
export default ExternalWidget
See the article here
If you haven't used styles in react before, it's quite simple. Export the styles that you reference into one component, or separately as text.js/colors.js etc, and reference the component class using a variable.
E.g if var s references your stylesheet then s.text1 will fetch the text1 class..
Good luck!
constructor(props){
super(props);
this.html = this.props.data.terms_and_conditions + '<style>body{color:#464647}p,li{text-align:justify}a{color:#444}<style>'
}
render() {
return (
<View style={[styles.flex, { marginHorizontal:15 }]}>
<WebView
source={{ html: this.html }}
/>
</View>
);
}
Use this, Resolved for me.
<HTMLView
stylesheet={htmlStyles}
textComponentProps={{ style: {color:'red'} }}
value={content} />

How to apply react-native-linear-gradient for the entire app background with React Native

I'm working on a React Native app. I can get the results I want by wrapping every single one of my container components in something like this:
<LinearGradient
style={{ flex: 1 }}
colors={[primary, primaryGradient2, primaryGradient1]}
locations={locations}
>
<ContentHere></ContentHere>
</LinearGradient>
What I would like to do is keep my project DRY and not have to repeat doing this for every container level component.
I tried making a component that returns the LinearGradient with the proper config I'd like based off a theme object, something like this:
<GradientTheme theme={theme}>
<MainContainer>Stuff here </MainContainer>
</ GradientTheme>
However, this renders without any of the MainContainer elements at all. The code for GradientTheme is as follows:
import React from 'react';
import PropTypes from 'prop-types';
import LinearGradient from 'react-native-linear-gradient';
export const GradientTheme = ({ theme }) => {
const { primary, primaryGradient2, primaryGradient1, locations } =
theme;
return (
<LinearGradient
style={{ flex: 1 }}
colors={[primary, primaryGradient2, primaryGradient1]}
locations={locations}
/>
);
};
GradientTheme.propTypes = {
theme: PropTypes.object
};
Just some extra info, I'm also using React-Navigation. A more ideal solution would be having a global way to apply all of this styling and theming.
However, this renders without any of the MainContainer elements at all.
You need to use this.props.children
<LinearGradient
style={{ flex: 1 }}
colors={[primary, primaryGradient2, primaryGradient1]}
locations={locations}>
//add this:
{this.props.children}
</LinearGradient>
It's a prop passed to all components using an opening and closing tag and contains whatever is between the opening and closing tags.
I'm not totally clear on exactly what you're trying to accomplish, but I suspect you would be best served by placing the <GradientTheme/> component at or near the root of your app, and managing the theme either locally in it's parent component state or with redux. In case you need it, here is a nice series of tutorials on using redux and react-navigation together in react-native.

How to use JSX spread attributes to pass arbitrary props to my controlled component?

In a controlled component, how do I pass arbitrary props to the render function? I think I need to make use of a constructor but I am getting "props is not defined" and other errors.
import * as React from 'react';
import { View } from 'react-native';
import styles from './Styles';
export default class MyView extends React.Component {
constructor(????) {
// What do I do so I can use {...props} in the render function below?
}
render() {
return (
<View style={styles.wrap} {...props}>
<View style={styles.main}>
{this.props.children}
</View>
</View>
);
}
};
I want to be able to do...
<MyView arbitraryprop="123" />
...and have arbitraryprop get passed to MyView::render().
The default constructor of <Component> does already initialize this.props. If the only thing you are doing in your component's constructor is to initialize your props you can leave the constructor out completely. Otherwise you have to call the super constructor with the props:
constructor(props) {
// call the super constructor
super(props);
// do your additional initialization, e.g. set initial state
}
Also your example can't work properly as you did not initialize the local variable props inside your render() function. It has to look something like this:
render() {
const {children, ...props} = this.props;
return (
<View style={styles.wrap} {...props}>
<View style={styles.main}>
{children}
</View>
</View>
);
}
You must use the proper scope when referencing props. In other words, this is a class so props isn't defined in the render function, but this.props is. Add this. to the beginning and it will work. (e.g. {...this.props}
<View style={styles.wrap} {...this.props}>

Background color covers only a half of the drawer

I am using react-navigation. In DrawerContent I've added all needed things to make drawer's background green while it covers only a half of the drawer. Here is the code and image of the drawer.
Here is the image
import React, { Component } from "react";
import { View, ScrollView,Button,Text,StyleSheet } from "react-native";
import { DrawerItems } from 'react-navigation';
const DrawerContent = (props) => (
<View style={style.container}>
<DrawerItems {...props} />
</View>
);
const style = StyleSheet.create({
container: {
flex:1
height: null,
backgroundColor: '#00FF00',
},
});
export default DrawerContent;
I believe the problem lies with the DrawerContent object. You should try giving that object the style that you want...
You are creating an object DrawerContent with the object View inside of it and giving it the style. In this case the View object might be smaller than the DrawerContent object...

Hiding the status bar with React Native

How do you hide the status bar for iOS or Android when developing with React Native? I've imported StatusBar, but I believe there is also StatusBarIOS and a StatusBar for Android.
Figured out how to hide the status bar. First of all, StatusBarIOS is deprecated so you need to import StatusBar and then simply include this code snippet at the top of your render:
<StatusBar hidden />
React Native Docs on StatusBar
You can invoke this method from anywhere in your component:
import React, { Component } from 'react';
import { StatusBar } from 'react-native';
class MyComponent extends Component {
componentDidMount() {
StatusBar.setHidden(true);
}
}
EDIT:
This will hide the status bar for the entire app and not just in your specific component, to solve this you can do:
componentWillUnmount() {
StatusBar.setHidden(false);
}
Or calling this method with false from somewhere else.
For Hidden:
StatusBar.setHidden(true, 'none');
For Show:
StatusBar.setHidden(false, 'slide');
I prefer the simple way of importing the StatusBar component and passing true to hidden prop...
So Simply:
import React from "react";
import { StatusBar, View, Text } from "react-native";
class App extends React.Component {
render() {
return (
<View>
<StatusBar hidden={true} />
<Text>Hello React Native!</Text>
</View>
)
}
}
From version 0.?? to current (0.55 / June 2018)
<StatusBar hidden />
Credit to the first comment in this answer
Remember to first import the StatusBar component as per the other answers here
If your reason for hiding it is to prevent your components from overlapping it, you might prefer to just use SafeAreaView as follows:
<SafeAreaView style={{flex: 1, backgroundColor: '#fff'}}>
<View style={{flex: 1}}>
<Text>Hello World!</Text>
</View>
</SafeAreaView>
It should be the parent component of a screen and can optionally use a backgroundColor to match the color of your screen. Make sure to set a flex attribute. Your components will now just take up any area not being used by the status bar. This is especially useful in getting around the 'notch' issue with some of the newer phones.
SafeAreaView is a component of react-native so you will need to make sure you add it to your imports:
import { SafeAreaView, Text, View } from 'react-native';
to make it transparent on android you can do this
<StatusBar backgroundColor={'#ffffff00'} />
{Platform.OS === 'ios' && <StatusBar barStyle="light-content" />}
also <StatusBar hidden /> is hidden it but you may see a margin on top
It hasn't worked doesn't matter what you have tried?
Maybe there is another <StatusBar hidden="false"> in your code. And it is deeper than your definition. This will replace your previous hidden="true" setting.
<View>
<StatusBar hidden={true} /> // this will be replaced by the deeper StatusBar tag
<View>
<StatusBar hidden={false} /> // remove this or put your `hidden="true"` here
</View>
</View>

Resources