React Native - Change Stylesheet Dynamically - react-native

Im coding a project in react native which requires rtl support (english and arabic). I however am experimenting with my own code rather then using a rtl plugin. Straight to the point i want to know is it possible that i can dynamically change stylesheet , so that all classes where : position:absolute. If it has a property left or right, to inverse it?
Fo example if a class is positioned absolutely, and has the :
Left:20,
Can i automagically make this become :
Right:20,
And same for the other way around? Is this possible in react native.

Yes, you can do something like
const styles = StyleSheet.create({
yourView: {
left: isRight ? 0 : 20,
right: isRight? 20: 0,
},
});
Where isRight is a variable you've defined previously
Another approach would be to create to separate it into different styles and attach the styles on render. Something like
const styles = StyleSheet.create({
yourView: {
...common styles here
},
leftView: {
left: 20,
},
rightView: {
right: 0,
},
});
Then in the render method u would attach the styles as:
render() {
return (
<View styles={[styles.mainView, isRight ? styles.leftView : styles.rightView]}/>
)
}

Related

Is there a "special" syntax for adding styles to children of a React Native component?

So, in regular CSS, if I want to apply a style to all children of elements with a given id/class/element name, I would do this:
(id/class/name of parent element) * {
}
This will automatically apply to every child of the specified parent element(s) - I don't need to add any classes/ids/names, etc. to the children.
Is there an equivalent syntax in React Native? Best I can do so far is
view {
// style here
}
subelementsOfView {
}
and then add the subelementsOfView style to all children of View.
Am I wrong? Is there another syntax I can use?
You would create a StyleSheet as shown:
export const styles = StyleSheet.create({
someStyleKey: {
backgroundColor: 'red'
}
})
Wherever your children components are you can do the following:
import { styles } from '../<location-of-stylesheet>'
// Using View as an example component
<View style={styles.someStyleKey}></View>
If your child component already has a style you can also pass in an array of styles such as:
<View style={[existingStyle, styles.someStyleKey]}></View>
Whatever comes later in the style array will overwrite previous style values.

Fluent UI React - how to apply global component styles with Fluent ThemeProvider

I'm working with the theming code below. I'm able to apply a global Fluent theme with the ThemeProvider and createTheme utility, but when I add component specific themes, I'm not getting any typings, which makes theming very difficult.
So my question is: how do I apply global component-specific styles using Fluent ThemeProvider with strong typing.
If, for example, I wanted to add a box shadow to all Fluent PrimaryButtons, I wouldn't know what properties to access on the components key passed into createTheme.
If you've done any global component theming, please let me know what pattern you used and if I'm on the right track, thanks!
import { createTheme } from '#fluentui/react';
import { PartialTheme } from '#fluentui/react-theme-provider';
// Trying to add global component styles (not getting typings)
const customComponentStyles = {
PrimaryButton: {
styles: {
root: {
background: 'red'
}
}
}
};
export const fluentLightTheme: PartialTheme = createTheme({
components: customComponentStyles, // Want to apply component styles
palette: {
themePrimary: '#0078d4',
themeLighterAlt: '#eff6fc',
themeLighter: '#deecf9',
themeLight: '#c7e0f4',
themeTertiary: '#71afe5',
themeSecondary: '#2b88d8',
themeDarkAlt: '#106ebe',
themeDark: '#005a9e',
themeDarker: '#004578',
neutralLighterAlt: '#faf9f8',
neutralLighter: '#f3f2f1',
neutralLight: '#edebe9',
neutralQuaternaryAlt: '#e1dfdd',
neutralQuaternary: '#d0d0d0',
neutralTertiaryAlt: '#c8c6c4',
neutralTertiary: '#a19f9d',
neutralSecondary: '#605e5c',
neutralPrimaryAlt: '#3b3a39',
neutralPrimary: '#323130',
neutralDark: '#201f1e',
black: '#000000',
white: '#ffffff'
}
});
The problem here is, that components is just a Record<string, ComponentStyles>, where ComponentStyles then just is a quite unspecific object of the form { styles?: IStyleFunctionOrObject<any, any>}. They would have to add an entry for every possible I<Component>Styles interface to ComponentsStyles, which I guess would be too much work and errorprone (e.g. forgetting to add a new component here ...).
Since all the I<Component>Styles interfaces are exported by Fluent, I always define the styles for each component separately and than merge them in the components object:
const buttonStyles: IButtonStyles = {
root: {
backgroundColor: 'red'
}
};
export const fluentLightTheme: PartialTheme = createTheme({
components: { PrimaryButton: { styles: buttonStyles } },
});
Here is a codepen example I created by using one of Fluent UIs basic Button examples: https://codepen.io/alex3683/pen/WNjmdWo

How to implement a user selectable theme / style in react native?

If user has selected light-theme, and switches to dark-theme, then all scenes will immediately render to using the dark-theme.
I am using react-native-router-flux if this helps.
Thanks in advance,
Different approaches are possible. One of them is to use React context. Generally it should be used with caution but theming is one of the official examples where it is suitable.
Theming is a good example of when you might want an entire subtree to have access to some piece of information
So the example might look like
class App extends Component {
getChildContext() {
return {theme: { primaryColor: "purple" }};
}
...
}
App.childContextTypes = {
theme: React.PropTypes.object
};
where you set the context for rest of your application and then you use it in your components
class Button extends Component {
render() {
return <TouchableHighlight underlayColor={this.context.theme.primaryColor}>...
}
}
Button.contextTypes = {
theme: React.PropTypes.object
};
If you want to switch theme, you can set context based on your state/props that can be based on user selection.
Currently we are dealing with same questions and therefore we have starting prototyping library called react-native-themeable.
The idea is to use context to store theme and (re)implement RN components so they can replace original components but supports theming.
Example of theme switching you can find in https://github.com/instea/react-native-themeable/blob/master/examples/src/SwitchTheme.js
You can start by creating one JS file containing two objects that have the colors for each theme. Then just use one as a default and if the user picks another theme save it in the database or using local storage and pull the relevant object.
export const theme1 = {
blue: '#fddd',
red: '#ddddd',
buttonColor: '#fff'
}
export const theme2 = {
blue: '#fddd',
red: '#ddddd'
buttonColor: '#fff'
}
Then just import the relevant file:
import * as themes from 'Themes'
const currentTheme = this.props.currentTheme ? this.props.currentTheme : 'theme1'
const styles = StyleSheet.create({
button: {
color: themes[currentTheme].buttonColor
},
})
Wouldn’t it be great if you could import a colors object directly, and modify the object every time you change the theme and have those new colors propagate to the entire app? Then your components wouldn’t need to have any special theme logic. I was able to achieve this using a combination of AsyncStorage, forced app restart, and async module loading.

React Native - What is the benefit of using StyleSheet vs a plain object?

What exactly is the benefit of using StyleSheet.create() vs a plain object?
const styles = StyleSheet.create({
container: {
flex: 1
}
}
Vs.
const styles = {
container: {
flex: 1
}
}
There is no benefit. Period.
Myth 1: StyleSheet is more performant
There is absolutely no performance difference between StyleSheet and an object declared outside of render (it would be different if you're creating a new object inside render every time). The performance difference is a myth.
The origin of the myth is likely because React Native team tried to do this, but they weren't successful. Nowhere in the official docs you will find anything about performance: https://facebook.github.io/react-native/docs/stylesheet.html, while source code states "not implemented yet": https://github.com/facebook/react-native/blob/master/Libraries/StyleSheet/StyleSheet.js#L207
Myth 2: StyleSheet validates style object at compile time
This is not true. Plain JavaScript can't validate objects at compile time.
Two things:
It does validate at runtime, but so does when you pass the style object to a component. No difference.
It does validate at compile time if you're using Flow or TypeScript, but so does once you pass the object as a style prop to a component, or if you properly typehint object like below. No difference either.
const containerStyle: ViewStyle = {
...
}
Quoting directly from comment section of StyleSheet.js of React native
Code quality:
By moving styles away from the render function, you're making the code easier to understand.
Naming the styles is a good way to add meaning to the low level components in the render function.
Performance:
Making a stylesheet from a style object makes it possible to refer to it by ID instead of creating a new style object every time.
It also allows to send the style only once through the bridge. All subsequent uses are going to refer an id (not implemented yet).
Also StyleSheet validates your stylesheet content as well. So any error of incorrect style property is shown at time of compiling rather than at runtime when StyleSheet is actually implemented.
The accepted answer is not an answer to the OP question.
The question is not the difference between inline styles and a const outside the class, but why we should use StyleSheet.create instead of a plain object.
After a bit of researching what I found is the following (please update if you have any info).
The advatanges of StyleSheet.create should be the following:
It validates the styles
Better perfomances because it creates a mapping of the styles to an ID, and then it refers inside with this ID, instead of creating every time a new object. So even the process of updating devices is faster because you don't send everytime all the new objects.
It used to be considered that using a StyleSheet was more performant, and was recommended for this reason by the RN team up until version 0.57, but it is now no longer recommended as correctly pointed out in another answer to this question.
The RN documentation now recommends StyleSheet for the following reasons, though I think these reasons would apply equally to plain objects that are created outside of the render function:
By moving styles away from the render function, you're making the
code easier to understand.
Naming the styles is a good way to add meaning to the low level
components in the render function.
So what do I think are the possible benefits of using StyleSheet over plain objects?
1) Despite claims to the contrary my testing on RN v0.59.10 indicates that you do get some validation when calling StyleSheet.create() and typescript (and probably flow) will also report errors at compile time. Even without compile time checking I think it's still beneficial to do run time validation of styles before they are used for rendering, particularly where components that use those styles could be conditionally rendered. This will allow such errors to be picked up without having to test all rendering scenarios.
2) Given that StyleSheet is recommended by the RN team they may still have hopes of using StyleSheet to improve performance in future, and they may have other possible improvements in mind as well, for example:
3) The current StyleSheet.create() run-time validation is useful, but a bit limited. It seems to be restricted to the type checking that you would get with flow or typescript, so will pick up say flex: "1" or borderStyle: "rubbish", but not width: "rubbish" as that could be a percentage string. It's possible that the RN team may improve such validation in future by checking things like percentage strings, or range limits, or you could wrap StyleSheet.create() in your own function to do that more extensive validation.
4) By using StyleSheet you are perhaps making it easier to transition to third party alternatives/extensions like react-native-extended-stylesheet that offer more.
So, today, September of 2021, after reading all the answers and doing some researches, I created a summary about using Stylesheet instead of a plain object.
Based on React Documentation, you should use the stylesheet when the complexity starts to grow.
The style prop can be a plain old JavaScript object. That's what we usually use for example code.
As a component grows in complexity, it is often cleaner to use StyleSheet.create to define several styles in one place.
In the simulator, when using stylesheet will display an ERROR, and when using the plain object will display only a WARNING.
Based on item 2, it looks like it has some validation while compiling. (A lot of people say that’s a myth)
If you need to migrate for a third-party library in the future, for some of them like react-native-extended-stylesheet, if you are using stylesheet, it will be easier.
You have some methods and properties that boost the development. For example, the property StyleSheet.absoluteFill will do position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, or the method compose() will allow you to combine two styles, overriding it.
P.S.: The performance answer looks to be a myth.
My opinion?
Based on item 2 and 5, go to stylesheet instead of plain objects.
I did not find any differences between StyleSheet and plain object, except of typing validation in TypeScript.
For example, this (note the typing differences):
import { View, Text, Image, StyleSheet } from 'react-native';
import logo from './logo.svg';
export default class App extends Component {
render() {
return (
<View style={styles.someViewStyle}>
<Text style={styles.someTextStyle}>Text Here</Text>
<Image style={styles.someImageStyle} source={logo} />
</View>
);
}
}
const styles: StyleSheet.create({
someViewStyle: {
backgroundColor: '#FFF',
padding: 10,
},
someTextStyle: {
fontSize: 24,
fontWeight: '600',
},
someImageStyle: {
height: 50,
width: 100,
},
});
equals to this:
import { View, Text, Image, ViewStyle, TextStyle, ImageStyle } from 'react-native';
import logo from './logo.svg';
export default class App extends Component {
render() {
return (
<View style={styles.someViewStyle}>
<Text style={styles.someTextStyle}>Text Here</Text>
<Image style={styles.someImageStyle} source={logo} />
</View>
);
}
}
const styles: {
someViewStyle: ViewStyle;
someTextStyle: TextStyle;
someImageStyle: ImageStyle;
} = {
someViewStyle: {
backgroundColor: '#FFF',
padding: 10,
},
someTextStyle: {
fontSize: 24,
fontWeight: '600',
},
someImageStyle: {
height: 50,
width: 100,
},
};
Creating your styles via StyleSheet.create will pass though validation only when global variable __DEV__ is set to true (or while running inside Android or IOS emulators see React Native DEV and PROD variables)
The function source code is pretty simple:
create < +S: ____Styles_Internal > (obj: S): $ReadOnly < S > {
// TODO: This should return S as the return type. But first,
// we need to codemod all the callsites that are typing this
// return value as a number (even though it was opaque).
if (__DEV__) {
for (const key in obj) {
StyleSheetValidation.validateStyle(key, obj);
if (obj[key]) {
Object.freeze(obj[key]);
}
}
}
return obj;
}
I would recommend using it because it performs run-time validation during development, also it freezes the object.
i know that this is a really late answer, but I've read that it shows you errors and provides auto completion in editors when you use StyleSheet.

Screen width in React Native

How do I get screen width in React native?
I need it because I use some absolute components that overlap and their position on screen changes with different devices.
In React-Native we have an Option called Dimensions
Include Dimensions at the top var where you have include the Image,and Text and other components.
Then in your Stylesheets you can use as below,
ex: {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height
}
In this way you can get the device window and height.
Simply declare this code to get device width
let deviceWidth = Dimensions.get('window').width
Maybe it's obviously but, Dimensions is an react-native import
import { Dimensions } from 'react-native'
Dimensions will not work without that
April 10th 2020 Answer:
The suggested answer using Dimensions is now discouraged. See: https://reactnative.dev/docs/dimensions
The recommended approach is using the useWindowDimensions hook in React; https://reactnative.dev/docs/usewindowdimensions which uses a hook based API and will also update your value when the screen value changes (on screen rotation for example):
import {useWindowDimensions} from 'react-native';
const windowWidth = useWindowDimensions().width;
const windowHeight = useWindowDimensions().height;
Note: useWindowDimensions is only available from React Native 0.61.0: https://reactnative.dev/blog/2019/09/18/version-0.61
If you have a Style component that you can require from your Component, then you could have something like this at the top of the file:
const Dimensions = require('Dimensions');
const window = Dimensions.get('window');
And then you could provide fulscreen: {width: window.width, height: window.height}, in your Style component. Hope this helps
React Native Dimensions is only a partial answer to this question, I came here looking for the actual pixel size of the screen, and the Dimensions actually gives you density independent layout size.
You can use React Native Pixel Ratio to get the actual pixel size of the screen.
You need the import statement for both Dimenions and PixelRatio
import { Dimensions, PixelRatio } from 'react-native';
You can use object destructuring to create width and height globals or put it in stylesheets as others suggest, but beware this won't update on device reorientation.
const { width, height } = Dimensions.get('window');
From React Native Dimension Docs:
Note: Although dimensions are available immediately, they may change (e.g due to >device rotation) so any rendering logic or styles that depend on these constants >should try to call this function on every render, rather than caching the value >(for example, using inline styles rather than setting a value in a StyleSheet).
PixelRatio Docs link for those who are curious, but not much more there.
To actually get the screen size use:
PixelRatio.getPixelSizeForLayoutSize(width);
or if you don't want width and height to be globals you can use it anywhere like this
PixelRatio.getPixelSizeForLayoutSize(Dimensions.get('window').width);
React Native comes with "Dimensions" api which we need to import from 'react-native'
import { Dimensions } from 'react-native';
Then,
<Image source={pic} style={{width: Dimensions.get('window').width, height: Dimensions.get('window').height}}></Image>
Only two simple steps.
import { Dimensions } from 'react-native' at top of your file.
const { height } = Dimensions.get('window');
now the window screen height is stored in the height variable.
Just discovered react-native-responsive-screen repo here. Found it very handy.
react-native-responsive-screen is a small library that provides 2 simple methods so that React Native developers can code their UI elements fully responsive. No media queries needed.
It also provides an optional third method for screen orienation detection and automatic rerendering according to new dimensions.
First get Dimensions from react-native
import { Dimensions } from 'react-native';
then
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
in windowWidth you will find the width of the screen while in windowHeight you will find the height of the screen.
The latest method from 2020 is to use useWindowDimensions
the way i implemented it-
make a global.js file and set window width and height as global variables
const WindowDimensions= (()=>{
global.windowWidth = useWindowDimensions().width;
global.windowHeight = useWindowDimensions().height;
return (<></>);
})
in App.js file,import window dimensions and add it to return block
use width and height everywhere as global.windowHeight and global.windowWidth
using global variables is not a good design pattern. but this thing works
I think using react-native-responsive-dimensions might help you a little better on your case.
You can still get:
device-width by using and responsiveScreenWidth(100)
and
device-height by using and responsiveScreenHeight(100)
You also can more easily arrange the locations of your absolute components by setting margins and position values with proportioning it over 100% of the width and height
You can achieve this by creating a component and using it by importing it into the file you need.
import {Dimensions, PixelRatio} from "react-native";
const {width, height} = Dimensions.get("window");
const wp = (number) => {
let givenWidth = typeof number === "number" ? number : parseFloat(number);
return PixelRatio.roundToNearestPixel((width * givenWidth) / 100);
};
const hp = (number) => {
let givenHeight = typeof number === "number" ? number : parseFloat(number);
return PixelRatio.roundToNearestPixel((height * givenHeight) / 100);
};
export {wp, hp};
Now, you should use it.
import { hp, wp } from "../<YOUR PATH>";
buttonContainer: {
marginTop: hp("2%"),
height: hp("7%"),
justifyContent: "center",
alignSelf: "center",
width: wp("70%"),
backgroundColor: "#1C6AFD",
borderRadius: 5,
},
buttonText: {
fontSize: wp("3.5%"),
color: "#dddddd",
textAlign: "center",
fontFamily: "Spartan-Bold",
}
That way, you will make your design responsive. I suggest using simple pixels in the styling of circle things like avatar images, etc.
In other cases, the above code wraps components according to the density pixels of the screen.
If you have any better solution, please comment.
First, you must import Dimensions from 'react-native'
import { View, StyleSheet, Dimensions } from "react-native";
after that, you can save width and height in variables:
const windowsWidth = Dimensions.get('window').width
const windowsHeight = Dimensions.get('window').height
them you could use both as you need, i.e. in styles:
flexDirection: windowsWidth<400 ? 'column' : 'row',
Remember this, your object styles is outside your component, so the cariable declaration must be outside your component too. But if you need it inside your component, no problem, can use it:
<Text> Width: { windowsWidth }</Text>
<Text> Height: { windowsHeight }</Text>
you can get device width and height in React Native, by the following code:
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
docs: https://reactnative.dev/docs/dimensions
import {useWindowDimensions, Dimensions} from 'react-native'
let width1= useWindowDimensions().width // Hook can be called only inside functional component, tthis is dynamic
let width2=Dimensions.get("screen").width