react-navigation / react-navigation

Routing and navigation for your React Native apps
https://reactnavigation.org
23.33k stars 4.98k forks source link

[Question] React Navigation get stack header height #2411

Closed James2516 closed 6 years ago

James2516 commented 6 years ago

How do i get the height of the StackNavigator header programmatically?

I need to detect if component A's position is within component B when pan gesture (drag) on A is released. However, the position from onPanResponderRelease's gesture.moveY is measured with respect to the entire screen, whereas the position returned from A's onLayoutis measured with respect to the parent view. So i'd need to know the parent view's current height to reconcile the differences.

spencercarli commented 6 years ago

You can access the header height like this

import { Header } from 'react-navigation';
Header.HEIGHT;

Source

neokaiyuan commented 6 years ago

I am getting this warning now. Seems like the approved method will fail soon.

Header.HEIGHT is deprecated and will be removed before react-navigation comes out of beta.

Any other way to get height of StackNavigator header?

neokaiyuan commented 6 years ago

So far I've been following hard-coded values here to avoid the warning from react navigation https://stackoverflow.com/questions/45698607/programmatically-retrieve-header-component-height-react-native

spencercarli commented 6 years ago

That's a good question, @kaiyuanneo. We've made some changes because the header wasn't working correctly in landscape and now the iPhoneX changes things.

@brentvatne added the deprecation warning - could you add any insight on how we could handle this question?

Once we determine that we should update the docs in some way

jordanmkoncz commented 6 years ago

Also wondering what the 'correct' method is now for getting the height of the header.

I'm using Header.HEIGHT in order to set this value as the width of my custom header button, i.e. to make my custom header button square, since it will automatically fill the full height of the header and I want it to always be a consistent 1:1 square aspect ratio.

brentvatne commented 6 years ago

@spencercarli - Header.HEIGHT was initially removed when @davepack landed SafeAreaView because the height is dynamic depending on orientation and Header.HEIGHT is a constant. I added it back because I didn't want to outright break apps without a deprecation warning well in advance. We need a solution that lets components subscribe to the get the current header height and also subscribe to changes, until then we can leave Header.HEIGHT as in. Open for ideas for how to solve this.

jordanmkoncz commented 6 years ago

@brentvatne you mentioned that the height of the header is dynamic depending on orientation - does it also depend on the height of the status bar? I was looking into solutions for getting the current height of the status bar yesterday and saw that on iPhone X the behaviour of the status bar is different; it appears that it always has a height of 44pt, while on other iOS devices it has a height of 20pt by default, which can increase to 40pt (not 44pt), e.g. during a phone call.

spencercarli commented 6 years ago

@jordanmkoncz I believe so

xavierlefevre commented 6 years ago

@brentvatne Why don't you still export appBarHeight ?

lukewlms commented 6 years ago

@brentvatne What about adding a property onContainerLayout for the header nav props? Seems this might be fairly simple since React Native provides onLayout on all views, so it would just be a matter of passing the provided callback to the top-level View container of the header.

This seems to be needed for our app b/c KeyboardAvoidingView needs to know the top offset of the view, which is at the bottom of the StackNavigator. So I don't yet see how we can use react-navigation with a KeyboardAvoidingView, which is quite a strong limitation.

elliotfleming commented 6 years ago

This is working for me...

import {
  Dimensions,
  KeyboardAvoidingView,
  View,
} from 'react-native';
import React, { Component } from 'react';
import PropTypes from 'prop-types';

const { height: fullHeight } = Dimensions.get('window');

export default class LayoutView extends Component {

  static propTypes = {
    children: PropTypes.node.isRequired,
  }

  state = {
    offset: 0,
  }

  onLayout = ({
    nativeEvent: { layout: { height } },
  }) => {
    const offset = fullHeight - height;
    this.setState({ offset });
  }

  render = () => (
    <View style={{ flex: 1 }} onLayout={this.onLayout}>
      <KeyboardAvoidingView
        style={{ flex: 1 }}}
        behavior="padding"
        keyboardVerticalOffset={this.state.offset}
      >
        {this.props.children}
      </KeyboardAvoidingView>
    </View>
  )

}
brentvatne commented 6 years ago

@jordanmkoncz - yup, and status bar matters too!

@elliotfleming - seems reasonable.

I left Header.HEIGHT exposed until we have a better solution. please create a RFC if you care about solving this problem

igor90007 commented 6 years ago

my way: navigationOptions:({navigation})=>{ return{
headerStyle:{marginTop:-30} } }`

javiio commented 6 years ago

These are the helpers that I use:

import { Dimensions, DeviceInfo, Platform } from 'react-native';
import { Header } from 'react-navigation';

export const LANDSCAPE = 'landscape';
export const PORTRAIT = 'portrait';

export const getHeaderHeight = () => {
  let height;
  const orientation = getOrientation();
  height = getHeaderSafeAreaHeight();
  height += DeviceInfo.isIPhoneX_deprecated && orientation === PORTRAIT ? 24 : 0;

  return height;
};

// This does not include the new bar area in the iPhone X, so I use this when I need a custom headerTitle component
export const getHeaderSafeAreaHeight = () => {
  const orientation = getOrientation();
  if (Platform.OS === 'ios' && orientation === LANDSCAPE && !Platform.isPad) {
    return 32;
  }
  return Header.HEIGHT;
};

export const getOrientation = () => {
  const { width, height } = Dimensions.get('window');
  return width > height ? LANDSCAPE : PORTRAIT;
};
Raunak-Agrawal commented 4 years ago

Header.height is not accessible via react-navigation. Any update on this?

tylergasperlin commented 4 years ago

@Raunak-Agrawal just import as below

import { Header } from 'react-navigation-stack';

console.log(Header.HEIGHT)

Prosquid1 commented 4 years ago

Have you tried what you posted @tylerg509 It's been removed in react-navigation-stack

phliem commented 4 years ago

'Header.HEIGHT' is depreciated in v2

You can now use the 'useHeaderHeight' hook or 'HeaderHeightContext'

import { useHeaderHeight } from 'react-navigation-stack';
const headerHeight = useHeaderHeight();
console.warn(headerHeight)
kiwipxl commented 4 years ago

Using useHeaderHeight in the render() function of a react-native Component is giving me the following error: "Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component." What could I be doing wrong? It's in the render function.

sharifhh commented 4 years ago

Using useHeaderHeight in the render() function of a react-native Component is giving me the following error: "Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component." What could I be doing wrong? It's in the render function.

You need to write functional components or HOC, you cannot use hooks in class components.

DnEgorWeb commented 4 years ago

From the documentation: use hook for functional components to get header's height (please be sure you call useHeaderHeight in body of your functional component and not inside of callbacks etc)

import { useHeaderHeight } from '@react-navigation/stack';

const headerHeight = useHeaderHeight();

or you can use HeaderHeightContext with React's Context API

import { HeaderHeightContext } from '@react-navigation/stack';

<HeaderHeightContext.Consumer>
  {headerHeight => (
    /* render something */
  )}
</HeaderHeightContext.Consumer>
mi-mazouz commented 4 years ago

I have lot of trouble using useHeaderHeight()! It causes some UI regressions as the height suddenly change for nothing in particular. It also takes time to be calculate the right height so I can see the content of my screen move down after a quick time on my screen

I decided to keep the Header.HEIGHT constant for now

squarfed commented 4 years ago

I have the same problem as @mi-mazouz, header height updating during render with a jerk in the UI. My header is transparent.

5ervant commented 4 years ago

This is working for me...

import {
  Dimensions,
  KeyboardAvoidingView,
  View,
} from 'react-native';
import React, { Component } from 'react';
import PropTypes from 'prop-types';

const { height: fullHeight } = Dimensions.get('window');

export default class LayoutView extends Component {

  static propTypes = {
    children: PropTypes.node.isRequired,
  }

  state = {
    offset: 0,
  }

  onLayout = ({
    nativeEvent: { layout: { height } },
  }) => {
    const offset = fullHeight - height;
    this.setState({ offset });
  }

  render = () => (
    <View style={{ flex: 1 }} onLayout={this.onLayout}>
      <KeyboardAvoidingView
        style={{ flex: 1 }}}
        behavior="padding"
        keyboardVerticalOffset={this.state.offset}
      >
        {this.props.children}
      </KeyboardAvoidingView>
    </View>
  )

}

@elliotfleming Will that work with a transparent header? (https://reactnavigation.org/docs/stack-navigator/#headerbackground) It seems not because a transparent header don't have height, right?

Take note, don't ask me to use HeaderHeightContext nor useHeaderHeight, they're raising an error on my Next.js app because that app and my mobile app share some components. https://github.com/expo/expo-cli/issues/1782

kdpadhiyar commented 4 years ago

Using useHeaderHeight in the render() function of a react-native Component is giving me the following error: "Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component." What could I be doing wrong? It's in the render function.

You need to write functional components or HOC, you cannot use hooks in class components.

how can i get header height in class component i am using React navigation v5 help me @sharifhh

sharifhh commented 4 years ago

Using useHeaderHeight in the render() function of a react-native Component is giving me the following error: "Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component." What could I be doing wrong? It's in the render function.

You need to write functional components or HOC, you cannot use hooks in class components.

how can i get header height in class component i am using React navigation v5 help me @sharifhh

@kdpadhiyar

export const withHeight = (Component) => { 

const height = useHeaderHeight();

return <Component height={height} />

}
Class Test ....

export default withHeight(Test);

you can see the height in Test component as this.props.height 😄

martinezguillaume commented 3 years ago

Same problem as @mi-mazouz and @PositronicBrain During first render, I receive 2 difference value with useCurrentHeight making a flicker. The first one is wrong, and the second is the right one.

I'm using headerTransparent too

kdpadhiyar commented 3 years ago

@sharifhh thank you so much for help.

robizzt commented 3 years ago

I get this error when I try to use useHeaderHeight(): Error: Couldn't find the header height. Are you inside a screen in Stack?

Code:

//...

import {
    createStackNavigator,
    useHeaderHeight
} from '@react-navigation/stack';

//...

const AppStack = createStackNavigator();
const AppStackNavigation = () => {
    const headerHeight = useHeaderHeight();  // <-- ERROR
    console.log(headerHeight);
    render (
        //...
    )
}
kamalpandey commented 3 years ago

How to use this in class component?

neclamis1285 commented 3 years ago

You can use this in a class component by wrapping your render contents in a 'HeaderHeightContext.Consumer'

import {HeaderHeightContext} from '@react-navigation/stack';

 render = () => {
    return (
        <HeaderHeightContext.Consumer>
            {(headerHeight: any) => {
                return (
                    <View>
                        <Text>Some Text</Text>
                    </View>
                    );
            }}
        </HeaderHeightContext.Consumer>
    );
};
warrenalphonso commented 3 years ago

I get this error when I try to use useHeaderHeight(): Error: Couldn't find the header height. Are you inside a screen in Stack?

Code:

//...

import {
  createStackNavigator,
  useHeaderHeight
} from '@react-navigation/stack';

//...

const AppStack = createStackNavigator();
const AppStackNavigation = () => {
  const headerHeight = useHeaderHeight();  // <-- ERROR
  console.log(headerHeight);
  render (
      //...
  )
}

Does anyone have a solution to this? I'm getting the same error.

neclamis1285 commented 3 years ago

Is this in a React Native Class component or a Function component?

warrenalphonso commented 3 years ago

Thanks for the quick reply! Function component.

neclamis1285 commented 3 years ago

I don't see any reason why you would receive an error there. I tried it in my app and it works as expected, no errors. What version of React Navigation stack are you using?

The version I am on is "@react-navigation/stack": "^5.7.1",

warrenalphonso commented 3 years ago

I was using createNativeStackNavigator instead of createStackNavigator, my bad! Otherwise it works as expected. Thanks @neclamis1285.

neclamis1285 commented 3 years ago

Awesome. Glad you got it working.

lyseiha commented 3 years ago

how to add more height to current height?

warrenalphonso commented 3 years ago

@lyseiha Have you tried the suggestions in #5936 and #283?

nicotroia commented 3 years ago

How would you get the header height, ie. use useHeaderHeight or HeaderHeightContext in a plain helper function? Since it is neither a "function component" nor within a class component

neclamis1285 commented 3 years ago

If I am understanding your question correctly, you could pass the header height from your function/class component to your utility function.

Class Component

import {HeaderHeightContext} from '@react-navigation/stack';

export default class MyClassComponent extends Component<Props, State> {

static contextType = HeaderHeightContext;

const {headerHeight} = this.context;
console.log('HEADER HEIGHT: ' + Utils.doSomethingWithHeaderHeight(headerHeight));

render = () => {
  <View style="{{height: headerHeight}}">
  </View>
};

}

Utility Function

export const Utils = {
   doSomethingWithHeaderHeight: (headerHeight) => {
        return headerHeight + 10;
    }
};
ibelgin commented 3 years ago

@neclamis1285 Just Wanted To Know Why You Are Increasing The Value By 10 In The Utility Function ?

kp72-dev commented 2 years ago

I get this error when I try to use useHeaderHeight(): Error: Couldn't find the header height. Are you inside a screen in Stack? Code:

//...

import {
    createStackNavigator,
    useHeaderHeight
} from '@react-navigation/stack';

//...

const AppStack = createStackNavigator();
const AppStackNavigation = () => {
    const headerHeight = useHeaderHeight();  // <-- ERROR
    console.log(headerHeight);
    render (
        //...
    )
}

Does anyone have a solution to this? I'm getting the same error.

I get this error when I try to use useHeaderHeight(): Error: Couldn't find the header height. Are you inside a screen in Stack? Code:

//...

import {
    createStackNavigator,
    useHeaderHeight
} from '@react-navigation/stack';

//...

const AppStack = createStackNavigator();
const AppStackNavigation = () => {
    const headerHeight = useHeaderHeight();  // <-- ERROR
    console.log(headerHeight);
    render (
        //...
    )
}

Does anyone have a solution to this? I'm getting the same error.

function headerHeight() { const height = useHeaderHeight(); return height } const height = headerHeight();

Kiran-infostretch commented 2 years ago

Check this solution: link

You just need to update your import, That's it !

React navigation V6

import { useHeaderHeight } from '@react-navigation/elements'; const headerHeight = useHeaderHeight(); React navigation V5

import { useHeaderHeight } from '@react-navigation/stack'; const headerHeight = useHeaderHeight(); or with React Context's API (not recommended)

React navigation V4

import { Header } from 'react-navigation-stack'; const headerHeight = Header.HEIGHT; React navigation V2-V3

import { Header } from 'react-navigation'; const headerHeight = Header.HEIGHT;

github-actions[bot] commented 2 years ago

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

AlexMNet commented 2 years ago

@Kiran-infostretch I am still getting the same error some of the others are getting when trying to use useHeaderHeight()

Error: Couldn't find the header height. Are you inside a screen in a navigator with a header?

Has anyone else managed to figure this out? Any additional information would be greatly appreciated! "@react-navigation/elements": "^1.2.1",

github-actions[bot] commented 2 years ago

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

github-actions[bot] commented 2 years ago

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.

fcole90 commented 1 year ago

I get this error when I try to use useHeaderHeight(): Error: Couldn't find the header height. Are you inside a screen in Stack?

Code:

//...

import {
  createStackNavigator,
  useHeaderHeight
} from '@react-navigation/stack';

//...

const AppStack = createStackNavigator();
const AppStackNavigation = () => {
  const headerHeight = useHeaderHeight();  // <-- ERROR
  console.log(headerHeight);
  render (
      //...
  )
}

The hook is defined here: https://github.com/react-navigation/react-navigation/blob/5.x/packages/stack/src/utils/useHeaderHeight.tsx

If you know it's a safe operation, you can define your own hook as

import { HeaderHeightContext } from '@react-navigation/stack';

const useHeaderHeight = () => {
  const height = React.useContext(HeaderHeightContext);

  if (height === undefined) {
    return 0;
  }

  return height;
};

Then use it instead of the one provided by react-navigation 😉

github-actions[bot] commented 1 year ago

Hey! This issue is closed and isn't watched by the core team. You are welcome to discuss the issue with others in this thread, but if you think this issue is still valid and needs to be tracked, please open a new issue with a repro.