Related
I am using ol version 7.1 with react 18.04 and next 13
This is the code
import { Feature } from "ol";
import { Point } from "ol/geom";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import Map from "ol/Map";
import "ol/ol.css";
import { fromLonLat } from "ol/proj";
import OSM from "ol/source/OSM";
import VectorSource from "ol/source/Vector";
import Icon from "ol/style/Icon";
import Style from "ol/style/Style";
import View from "ol/View";
import type { FunctionComponent, PropsWithChildren } from "react";
import { useEffect, useRef } from "react";
export const Map: FunctionComponent<
PropsWithChildren<{ coordinates: number[] }>
> = ({ children, coordinates }) => {
const transformedCoordinates = fromLonLat(coordinates);
const mapElement = useRef<HTMLDivElement>(null);
const mapRef = useRef<Map>();
useEffect(() => {
const point = new Point(transformedCoordinates);
const feature = new Feature({ geometry: point });
const drawSource = new VectorSource({ wrapX: false, features: [feature] });
const svg = 'some svg'
const style = new Style({
image: new Icon({
opacity: 1,
src,
scale: 0.08,
}),
});
if (mapElement.current && !mapRef.current) {
mapRef.current = new Map({
target: mapElement.current ?? undefined,
layers: [
new TileLayer({
source: new OSM(),
}),
new VectorLayer({
source: drawSource,
style,
}),
],
view: new View({
center: transformedCoordinates,
zoom: 14,
}),
});
}
}, [transformedCoordinates]);
return (
<div
id="map"
className="map"
style={{ height: "100%", width: "100%" }}
ref={mapElement}
>
{children}
</div>
);
};
Used this way:
<div className="relative h-[250px] w-[350px]">
<Map coordinates={data} />
</div>
Using next dev is rendering the map totally fine:
But using next build, does not display the map, but just the outer style. What am I missing?
I tried adding some Scripts which I saw from ol, but the threads were pretty old and it did not change anything.
I do not know where ol gets the map styling etc. from. It seems like somehow this gets missing in build mode?
I am also using JOY ui & tailwind css, not sure if there are classnames which could override something in ol?
Try setting swcMinify to false in next.config. Since Next v13 swcMinify is true by default.
I'm new to react native development. And I have created this sample note application using Redux for react native. In this application user must be able to add a note and that note needs to be managed via redux store.
And I have created required action and reducer for adding note functionality.
However I was not able to add a new note to the store. I tried debugging and when it reaches the "ADD_Note" case in reducer, it jump into default case automatically. And whatever the string value or note that pass via action, it becomes 'undefined' in the reducer.
Please refer my code for adding note screen
import React, {useState, useEffect} from 'react';
import {
StyleSheet,
View,
Text,
TextInput,
Button,
FlatList,
} from 'react-native';
import {useSelector, useDispatch} from 'react-redux';
import * as NoteActions from '../store/actions/Note'; // import note actions
const NoteScreen = () => {
const [note, setNote] = useState('');
const dispatch = useDispatch(); // use dispatch
const noteHandler = text => {
setNote(text);
};
return (
<View style={styles.container}>
<View>
<TextInput
style={styles.textInput}
placeholder="Enter a note"
onChangeText={noteHandler}
value={note}
/>
<Button
title="Add"
onPress={() => {
console.log(note);
dispatch(NoteActions.addNote(note));
}}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
width: '100%',
margin: 10,
},
textInput: {
width: '100%',
marginBottom: 10,
textAlign: 'center',
borderBottomWidth: 1,
},
});
export default NoteScreen;
And below is the action js file.
// action types
export const ADD_NOTE = 'ADD_NOTE'
//action creators
export function addNote(note){
return{
type:ADD_NOTE,
date:note
}
}
And below is the reducer js file
// initial state
const initialState ={
noteList:[]
}
// import actions
import ADD_NOTE from '../actions/Note';
function noteReducer (state=initialState, action){
debugger;
switch(action.type){
case ADD_NOTE:
const newNote = action.data
return{
...state,
noteList: state.noteList, newNote
}
default:
return state;
}
}
export default noteReducer;
Please help me.
Thanks in advance,
Yohan
You need to add layer of dispatch at your action, also watch the typo date instead data
export const addNote = (note) => (dispatch, getState) => {
return dispatch({
type:ADD_NOTE,
data :note
})
}
How can I get the previous URL in Next.js?
I thought the values this.props.router.asPath and nextProps.router.asPath are different.
Actually, I want to call router.push after login. I know that router.back goes to the previous page. But it's possible to go to another site. The users having history stacks go to the previous page, the users not having history stacks go to / main page.
import { Component } from 'react'
import App, { Container } from 'next/app';
import ErrorComponent from '#/components/error'
export default class MyApp extends App {
render() {
console.log(this.props)
const { Component, pageProps, router } = this.props;
const props = {
...pageProps,
router
}
return (
<ErrorBoundary>
<Container>
<Component {...props} />
</Container>
</ErrorBoundary>
);
}
componentWillReceiveProps(nextProps) {
// previous page url /contents
console.log(this.props.router.asPath) // /about
console.log(nextProps.router.asPath) // /about
console.log('window.history.previous.href', window.history.previous) // undefined
}
}
How can I fix it? Or how can I get the previous URL to move page after login?
You find the Referer ( so the previous URL ) in the context of getServerSideProps or any other Data fetching methods
as
context.req.headers.referer
example in code
export async function getServerSideProps(context) {
console.log(context.req.headers.referer)
}
I've used Context do to this
In _app.tsx
import { HistoryProvider } from '../contexts/History'
const MyApp: React.FC<AppProps> = ({ Component, pageProps }) => {
return (
<ThemeProvider theme={theme}>
<Header />
<HistoryProvider>
<Component {...pageProps} />
</HistoryProvider>...
/contexts/History.tsx
import { useRouter } from 'next/router'
import React, { createContext, useState, useEffect, useContext } from 'react'
interface HValidation {
history: string[]
setHistory(data: string[]): void
back(): void
}
const HistoryContext = createContext<HValidation>({} as HValidation)
export const HistoryProvider: React.FC = ({ children }) => {
const { asPath, push, pathname } = useRouter()
const [history, setHistory] = useState<string[]>([])
function back() {
for (let i = history.length - 2; i >= 0; i--) {
const route = history[i]
if (!route.includes('#') && route !== pathname) {
push(route)
// if you want to pop history on back
const newHistory = history.slice(0, i)
setHistory(newHistory)
break
}
}
}
useEffect(() => {
setHistory(previous => [...previous, asPath])
}, [asPath])
return (
<HistoryContext.Provider
value={{
back,
history,
setHistory,
}}
>
{children}
</HistoryContext.Provider>
)
}
export function useHistory(): HValidation {
const context = useContext(HistoryContext)
return context
}
In any component, you can use
import { useHistory } from '../../contexts/History'
const ContentHeader: React.FC<ContentHeaderProps> = ({ title, hideBack }) => {
const { history, back } = useHistory() ...
I've used this component to back history ignoring links with hash (#), because the native router.back() was bugging when i have <a href="#someid" /> to scroll page to some page ids
I wanted to go back to last page, and not the last anchor
EDIT 01/04/2021
You can also set a fallback route for "back".
back(fallbackRoute?: string): void
function back(fallbackRoute?: string) {
for (let i = history.length - 2; i >= 0; i--) {
const route = history[i]
console.log({ route, pathname })
if (!route.includes('#') && route !== pathname) {
push(route)
const newHistory = history.slice(0, i)
setHistory(newHistory)
return
}
}
if (fallbackRoute) {
router.push(fallbackRoute)
}
}
I think you can implement a custom history in global state
Something like this
_app.js
import React from 'react';
import App, { Container } from 'next/app';
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
}
state = {
history: [] // keep history items in state
};
componentDidMount() {
const { asPath } = this.props.router;
// lets add initial route to `history`
this.setState(prevState => ({ history: [...prevState.history, asPath] }));
}
componentDidUpdate() {
const { history } = this.state;
const { asPath } = this.props.router;
// if current route (`asPath`) does not equal
// the latest item in the history,
// it is changed so lets save it
if (history[history.length - 1] !== asPath) {
this.setState(prevState => ({ history: [...prevState.history, asPath] }));
}
}
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Component history={this.state.history} {...pageProps} />
</Container>
);
}
}
export default MyApp;
so then in your components you can navigate wherever you want within history
if (!history || !history.length) {
router.push('/');
} else {
router.push(history[history.length - 1]);
}
Hope this helps!
I was looking for a very simple way to do this since some of the answers here seem a bit complex for implementing something this simple. router.back() doesn't seem to work well in this scenario as, in my case, it'd go all the way back and out of my site some times.
So, I thought 🤔, what better way to do this than localStorage?
when I need to send the user to the '/login' route, I add the current route to localStorage
if (!auth.user) {
window.localStorage.setItem("path", router.asPath);
router.replace("/login");
return <div> redirecting to login... </div>;
}
and once the user sign-in, I send them back to the previous page (the route of which has been saved in the localStorage
if (auth.user) {
router.replace(localStorage.getItem("path") || "/");
return <div> Loading... </div>
);
}
You can observe the localStorage while testing to see what is going on. I hope this helps someone.
Let's say there's a /profile page which should be rendered iff user is logged in or else user should be redirected to /login, after user login on /login, it should be pushed to previous page (here/profile) but not on another website or New Tab.
In /profile this is how you should redirect to /login
Router.push('/login?referer=profile', '/login')
In /login after user is successfully logged in, use:
Router.push(Router.query.referer?.toString||'/')
Hope this helped.
I recently had this problem and used the following solution to route back to the previous page.
In my component I used the useRouter() hook from Next.js. This hook produces a router object which has the back() function. This function can be used on an <a> tag to redirect back in the following way.
const Component: React.FC = () => {
const router = useRouter();
return (
<>
<a onClick={() => router.back()}>Go back to the last page</a>
</>
);
};
Note that this function does not produce a URL that you can use as a value in the href, which is unfortunate. But I think this solution is simple yet effective.
Reference: https://nextjs.org/docs/api-reference/next/router#routerback
Simple Hook
Add this hook, and call it in your _app.tsx or where needed. You can compare it to router.pathname if you need to know what the change was.
const usePreviousRoute = () => {
const { asPath } = useRouter();
const ref = useRef<string | null>(null);
useEffect(() => {
ref.current = asPath;
}, [asPath]);
return ref.current;
};
This doesn't leave data behind once the page is closed (storage, cookies) and cleanly resets to null on next visit to the site.
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
// in _app.js
function MyApp({ Component, pageProps }) {
const router = useRouter();
const [history, setHistory] = useState({ previous: null, current: router.asPath });
useEffect(() => {
setHistory((oldHistory) => ({ ...oldHistory, previous: oldHistory.current, current: router.asPath }));
}, [router.asPath]);
return (<Component {...{ ...pageProps, history }} />);
}
export default MyApp;
// in a page
function MyPage({ ...pageProps }) {
return (<span>Previous route: {pageProps.history?.previous || "/"}</span>);
}
export default MyPage;
I tried doing similar to iurii's answer. My _app.js looks like this (I was trying to integrate with segment.com so felt this need)
export default class MyApp extends App {
componentDidMount () {
const { asPath } = this.props.router;
this.setState(prevState => ({ history: [...prevState.history, asPath] }));
const isBrowser = typeof window !== 'undefined';
if(isBrowser) {
// For the first page load
console.log("Going to log first load --> referrer : ", document.referrer);
// this can get me the document.referrer properly, if I come to the website from a third party source like google search.
global.analytics.page(window.location.href,
{referrer: document.referrer}
)
}
}
static async getInitialProps ({ Component, router, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
state = {
history: [] // keep history items in state
};
componentDidUpdate() {
const { history } = this.state;
const { asPath } = this.props.router;
// if current route (`asPath`) does not equal
// the latest item in the history,
// it is changed so lets save it
if (history[history.length - 1] !== asPath) {
global.analytics.page(window.location.href, {
referrer: history[history.length - 1] ? history[history.length - 1] : ""
})
// this simulates the document.referrer on pages after the user navigates
this.setState(prevState => ({ history: [...prevState.history, asPath] }));
}
}
So with a combination of history[history.length - 1] ? history[history.length - 1] : "" and const isBrowser = typeof window !== 'undefined'; I am able to simulate document.referrer for all cases. But I am missing one case, suppose , I am at google, my site landing page is A, then A points to B
Then google to A --> I get document.referrer as google
Then A to B --> I get document.referrer as A which is consistent with the behavior.
But Now If I refresh page B, then my document.referrer becomes google again.
I think I can save the last known previous URL in local storage , but that will be a anti-pattern as the browser back button can correctly take the user to the previous page (A), so somewhere the data is there already. Currently I can live with this solution as I only use this for analytics purpose on segment.com and google analytics, so refreshing will mess up my analytics numbers slightly, but still looking forward to a perfect solution so get exact data.
I can not get previous URL but with the code below I can find have a back URL or no:
typeof window !== 'undefined' && +window?.history?.state?.idx > 0
const back = async () => {
if (typeof window !== 'undefined' && +window?.history?.state?.idx > 0) {
await Router.back()
} else {
await Router.replace(fallbackURL)
}
}
A more simple way is
import { useRouter } from 'next/router';
const router = useRouter();
router.back()
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({
...
});
Finally, I've been able to do so. Don't know the performance issues it can carry, but they should not be a problem since it's only called on resizing or orientation change.
I've made a global controller where I have a function which receives the component (the container, the view) and adds an event listener to it:
const getScreenInfo = () => {
const dim = Dimensions.get('window');
return dim;
}
const bindScreenDimensionsUpdate = (component) => {
Dimensions.addEventListener('change', () => {
try{
component.setState({
orientation: isPortrait() ? 'portrait' : 'landscape',
screenWidth: getScreenInfo().width,
screenHeight: getScreenInfo().height
});
}catch(e){
// Fail silently
}
});
}
With this, I force to rerender the component when there's a change on orientation, or on window resizing.
Then, on every component constructor:
import ScreenMetrics from './globalFunctionContainer';
export default class UserList extends Component {
constructor(props){
super(props);
this.state = {};
ScreenMetrics.bindScreenDimensionsUpdate(this);
}
}
This way, it gets rerendered everytime there's a window resize or an orientation change.
You should note, however, that this must be applied to every component which we want to listen to orientation changes, since if the parent container is updated but the state (or props) of the children do not update, they won't be rerendered, so it can be a performance kill if we have a big children tree listening to it.
Hope it helps someone!
I made a super light component that addresses this issue.
https://www.npmjs.com/package/rn-orientation-view
The component re-renders it's content upon orientation change.
You can, for example, pass landscapeStyles and portraitStyles to display these orientations differently.
Works on iOS and Android.
It's easy to use. Check it out.
React Native also have useWindowDimensions hooks that returns the width and height of your device.
With this, you can check easily if the device is in 'Portrait' or 'Landscape' by comparing the width and height.
See more here
I had the same problem. After the orientation change the layout didn't change.
Then I understood one simple idea - layout should depend on screen width that should be calculated inside render function, i.e.
getScreen = () => {
return Dimensions.get('screen');
}
render () {
return (
<View style={{ width: this.getScreen().width }>
// your code
</View>
);
}
In that case, the width will be calculated at the moment of render.
** I am using this logic for my landscape and portrait Logic.**
** by this if I launch my app in landscape first I am getting the real height of my device. and manage the hight of the header accordingly.**
const [deviceOrientation, setDeviceOrientation] = useState(
Dimensions.get('window').width < Dimensions.get('window').height
? 'portrait'
: 'landscape'
);
const [deviceHeight, setDeviceHeight] = useState(
Dimensions.get('window').width < Dimensions.get('window').height
? Dimensions.get('window').height
: Dimensions.get('window').width
);
useEffect(() => {
const setDeviceHeightAsOrientation = () => {
if (Dimensions.get('window').width < Dimensions.get('window').height) {
setDeviceHeight(Dimensions.get('window').height);
} else {
setDeviceHeight(Dimensions.get('window').width);
}
};
Dimensions.addEventListener('change', setDeviceHeightAsOrientation);
return () => {
//cleanup work
Dimensions.removeEventListener('change', setDeviceHeightAsOrientation);
};
});
useEffect(() => {
const deviceOrientation = () => {
if (Dimensions.get('window').width < Dimensions.get('window').height) {
setDeviceOrientation('portrait');
} else {
setDeviceOrientation('landscape');
}
};
Dimensions.addEventListener('change', deviceOrientation);
return () => {
//cleanup work
Dimensions.removeEventListener('change', deviceOrientation);
};
});
console.log(deviceHeight);
if (deviceOrientation === 'landscape') {
return (
<View style={[styles.header, { height: 60, paddingTop: 10 }]}>
<TitleText>{props.title}</TitleText>
</View>
);
} else {
return (
<View
style={[
styles.header,
{
height: deviceHeight >= 812 ? 90 : 60,
paddingTop: deviceHeight >= 812 ? 36 : 10
}
]}>
<TitleText>{props.title}</TitleText>
</View>
);
}
I have, by far, had the most success with this library: https://github.com/axilis/react-native-responsive-layout
It does what you are asking for and a lot more. Simple Component implementation without hardly any logic like some of the more complex answers above. My project is using Phone, Tablet, and web via RNW - and the implementation is flawless. Additionally when resizing the browser it's truly responsive, and not just on initial rendering - handling phone orientation changes flawlessly.
Example code (Put any components as children of blocks):
<Grid>
<Section> {/* Light blue */}
<Block xsSize="1/1" smSize="1/2" />
<Block xsSize="1/1" smSize="1/2" />
<Block xsSize="1/1" smSize="1/2" />
</Section>
<Section> {/* Dark blue */}
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
<Block size="1/1" smSize="1/2" />
</Section>
</Grid>
To give this:
I have written a HoC solution for my expo SDK36 project, it support orientation change and pass props.orientation based on ScreenOrientation.Orientation value.
import React, { Component } from 'react';
import { ScreenOrientation } from 'expo';
export default function withOrientation(Component) {
class DetectOrientation extends React.Component {
constructor(props) {
super(props);
this.state = {
orientation: '',
};
this.listener = this.listener.bind(this);
}
UNSAFE_componentWillMount() {
this.subscription = ScreenOrientation.addOrientationChangeListener(this.listener);
}
componentWillUnmount() {
ScreenOrientation.removeOrientationChangeListener(this.subscription);
}
listener(changeEvent) {
const { orientationInfo } = changeEvent;
this.setState({
orientation: orientationInfo.orientation.split('_')[0],
});
}
async componentDidMount() {
await this.detectOrientation();
}
async detectOrientation() {
const { orientation } = await ScreenOrientation.getOrientationAsync();
this.setState({
orientation: orientation.split('_')[0],
});
}
render() {
return (
<Component
{...this.props}
{...this.state}
onLayout={this.detectOrientation}
/>
);
}
}
return (props) => <DetectOrientation {...props} />;
}
To achieve a more performant integration, I used the following as a superclass for each of my react-navigation screens:
export default class BaseScreen extends Component {
constructor(props) {
super(props)
const { height, width } = Dimensions.get('screen')
// use this to avoid setState errors on unmount
this._isMounted = false
this.state = {
screen: {
orientation: width < height,
height: height,
width: width
}
}
}
componentDidMount() {
this._isMounted = true
Dimensions.addEventListener('change', () => this.updateScreen())
}
componentWillUnmount() {
this._isMounted = false
Dimensions.removeEventListener('change', () => this.updateScreen())
}
updateScreen = () => {
const { height, width } = Dimensions.get('screen')
if (this._isMounted) {
this.setState({
screen: {
orientation: width < height,
width: width, height: height
}
})
}
}
Set any root components to extend from this component, and then pass the screen state to your leaf/dumb components from the inheriting root components.
Additionally, to keep from adding to the performance overhead, change the style object instead of adding more components to the mix:
const TextObject = ({ title }) => (
<View style={[styles.main, screen.orientation ? styles.column : styles.row]}>
<Text style={[styles.text, screen.width > 600 ? {fontSize: 14} : null ]}>{title}</Text>
</View>
)
const styles = StyleSheet.create({
column: {
flexDirection: 'column'
},
row: {
flexDirection: 'row'
},
main: {
justifyContent: 'flex-start'
},
text: {
fontSize: 10
}
}
I hope this helps anyone in the future, and you'll find it to be quite optimal in terms of overhead.
I'm using styled-components, and this is how I re-render the UI on orientation change.
import React, { useState } from 'react';
import { View } from 'react-native';
import { ThemeProvider } from 'styled-components';
import appTheme from 'constants/appTheme';
const App = () => {
// Re-Layout on orientation change
const [theme, setTheme] = useState(appTheme.getTheme());
const onLayout = () => {
setTheme(appTheme.getTheme());
}
return (
<ThemeProvider theme={theme}>
<View onLayout={onLayout}/>
{/* Components */}
</ThemeProvider>
);
}
export default App;
Even if you're not using styled-components, you can create a state and update it on onLayout to re-render the UI.
This is my solution:
const CheckOrient = () => {
console.log('screenHeight:' + Dimensions.get('screen').height + ', screenWidth: ' + Dimensions.get('screen').width);
}
return ( <
View onLayout = {
() => CheckOrient()
} >
............
<
/View>
Note for the case with a pure component. #mridul-tripathi answer works correctly, but if a pure component is used, then probably only parent/top-level component reacting to orientation change is not enough. You will also need to update a pure component separately on orientation change.
All you need is:
import { useWindowDimensions } from 'react-native';
export default function useOrientation() {
const window = useWindowDimensions();
return window.height >= window.width ? 'portrait' : 'landscape';
}
You need useWindowDimensions
This hook re-render component when dimension change and apply styles but Dimensions object can't re-render component and change style, it just work in first render
import { useWindowDimensions } from 'react-native';
then destructure it
const { height, width } = useWindowDimensions();
and final you can do like this
import React from "react";
import { View, StyleSheet, useWindowDimensions } from "react-native";
const App = () => {
const { height, width } = useWindowDimensions();
const isPortrait = height > width;
return (
<View style={isPortrait ? styles.portrait : styles.landscape}>
{/* something */}
</View>
);
};
const styles = StyleSheet.create({
portrait: {},
landscape: {},
});
export default App;
also you can use scale property
const { scale } = useWindowDimensions();
read this document
https://reactnative.dev/docs/usewindowdimensions
I am new to React Native and Firebase, this is probably easy but I can't figure out what's wrong. I'm trying to:
(1) Fetch a list of items from my Firebase database, convert the snapshot.val() that Firebase returns into an array (DONE)
(2) Filter that array for when each object has a specific color (DONE)
(3) Send that filtered array of objects to a function that renders a JSX component to the screen (NOT WORKING)
PROBLEM - The console.log above the return() statement in renderItems() tells me that I am getting the data that I need to be there correctly, but for whatever reason, the JSX components are not rendering to the screen. I feel like there is something simple I am missing, but I just can't figure out what. Please help!
import React, { Component } from 'react';
import { ScrollView } from 'react-native';
import _ from 'lodash';
import firebase from 'firebase';
import Item from './Item';
class ItemList extends Component {
getItemsByColor(color) {
const itemsRef = firebase.database().ref('/items/');
itemsRef.once('value').then((snapshot) => {
const filteredItems = _.filter(snapshot.val(), item => {
return item.color === color;
});
this.renderItems(filteredItems);
}, (error) => {
// The Promise was rejected.
console.error(error);
});
}
renderItems(filteredItems) {
filteredItems.map((item) => {
console.log(item.name);
return <Item name={item.name} color={item.color} />;
});
}
render() {
return (
<ScrollView style={{ backgroundColor: '#333', flex: 1 }}>
{this.getItemsByColor('blue')}
</ScrollView>
);
}
}
export default ItemList;
Within renderItems() you are returning each <Item/> to the map function, but are not then returning the result of the function afterwards. Try including another return like so:
renderItems(filteredItems) {
return filteredItems.map((item) => {
console.log(item.name);
return <Item name={item.name} color={item.color} />;
});
}
You may need to then put in a couple more return statements in getItemsByColor() as well so that the array of <Item/>'s is returned to the function call within render().