I'm developing a React Native application to be deployed as a native application on iOS and Android (and Windows, if possible).
The problem is that we want the layout to be different depending on screen dimensions and its orientation.
I've made some functions that return the styles object and are called on every component render's function, so I am able to apply different styles at application startup, but if the orientation (or screen's size) changes once the app has been initialized, they aren't recalculated nor reapplied.
I've added listeners to the top rendered so it updates its state on orientation change (and it forces a render for the rest of the application), but the subcomponents are not rerendering (because, in fact, they have not been changed).
So, my question is: how can I make to have styles that may be completely different based on screen size and orientation, just as with CSS Media Queries (which are rendered on the fly)?
I've already tried react-native-responsive module without luck.
Thank you!
If using Hooks. You can refer to this solution: https://stackoverflow.com/a/61838183/5648340
The orientation of apps from portrait to landscape and vice versa is a task that sounds easy but may be tricky in react native when the view has to be changed when orientation changes. In other words, having different views defined for the two orientations can be achieved by considering these two steps.
Import Dimensions from React Native
import { Dimensions } from 'react-native';
To identify the current orientation and render the view accordingly
/**
* Returns true if the screen is in portrait mode
*/
const isPortrait = () => {
const dim = Dimensions.get('screen');
return dim.height >= dim.width;
};
/**
* Returns true of the screen is in landscape mode
*/
const isLandscape = () => {
const dim = Dimensions.get('screen');
return dim.width >= dim.height;
};
To know when orientation changes to change view accordingly
// Event Listener for orientation changes
Dimensions.addEventListener('change', () => {
this.setState({
orientation: Platform.isPortrait() ? 'portrait' : 'landscape'
});
});
Assembling all pieces
import React from 'react';
import {
StyleSheet,
Text,
Dimensions,
View
} from 'react-native';
export default class App extends React.Component {
constructor() {
super();
/**
* Returns true if the screen is in portrait mode
*/
const isPortrait = () => {
const dim = Dimensions.get('screen');
return dim.height >= dim.width;
};
this.state = {
orientation: isPortrait() ? 'portrait' : 'landscape'
};
// Event Listener for orientation changes
Dimensions.addEventListener('change', () => {
this.setState({
orientation: isPortrait() ? 'portrait' : 'landscape'
});
});
}
render() {
if (this.state.orientation === 'portrait') {
return (
//Render View to be displayed in portrait mode
);
}
else {
return (
//Render View to be displayed in landscape mode
);
}
}
}
As the event defined for looking out the orientation change uses this command ‘this.setState()’, this method automatically again calls for ‘render()’ so we don’t have to worry about rendering it again, it’s all taken care of.
Here's @Mridul Tripathi's answer as a reusable hook:
// useOrientation.tsx
import {useEffect, useState} from 'react';
import {Dimensions} from 'react-native';
/**
* Returns true if the screen is in portrait mode
*/
const isPortrait = () => {
const dim = Dimensions.get('screen');
return dim.height >= dim.width;
};
/**
* A React Hook which updates when the orientation changes
* @returns whether the user is in 'PORTRAIT' or 'LANDSCAPE'
*/
export function useOrientation(): 'PORTRAIT' | 'LANDSCAPE' {
// State to hold the connection status
const [orientation, setOrientation] = useState<'PORTRAIT' | 'LANDSCAPE'>(
isPortrait() ? 'PORTRAIT' : 'LANDSCAPE',
);
useEffect(() => {
const callback = () => setOrientation(isPortrait() ? 'PORTRAIT' : 'LANDSCAPE');
Dimensions.addEventListener('change', callback);
return () => {
Dimensions.removeEventListener('change', callback);
};
}, []);
return orientation;
}
You can then consume it using:
import {useOrientation} from './useOrientation';
export const MyScreen = () => {
const orientation = useOrientation();
return (
<View style={{color: orientation === 'PORTRAIT' ? 'red' : 'blue'}} />
);
}
You can use the onLayout prop:
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
screen: Dimensions.get('window'),
};
}
getOrientation(){
if (this.state.screen.width > this.state.screen.height) {
return 'LANDSCAPE';
}else {
return 'PORTRAIT';
}
}
getStyle(){
if (this.getOrientation() === 'LANDSCAPE') {
return landscapeStyles;
} else {
return portraitStyles;
}
}
onLayout(){
this.setState({screen: Dimensions.get('window')});
}
render() {
return (
<View style={this.getStyle().container} onLayout = {this.onLayout.bind(this)}>
</View>
);
}
}
}
const portraitStyles = StyleSheet.create({
...
});
const landscapeStyles = StyleSheet.create({
...
});