SafeAreaContext.tsx 2.5KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import * as React from 'react';
  2. import { StyleSheet } from 'react-native';
  3. import NativeSafeAreaView from './NativeSafeAreaView';
  4. import { EdgeInsets, InsetChangedEvent, Metrics, Rect } from './SafeArea.types';
  5. export const SafeAreaInsetsContext = React.createContext<EdgeInsets | null>(
  6. null,
  7. );
  8. export const SafeAreaFrameContext = React.createContext<Rect | null>(null);
  9. export interface SafeAreaViewProps {
  10. children?: React.ReactNode;
  11. initialMetrics?: Metrics | null;
  12. }
  13. export function SafeAreaProvider({
  14. children,
  15. initialMetrics,
  16. }: SafeAreaViewProps) {
  17. const parentInsets = useParentSafeAreaInsets();
  18. const parentFrame = useParentSafeAreaFrame();
  19. const [insets, setInsets] = React.useState<EdgeInsets | null>(
  20. initialMetrics?.insets ?? parentInsets ?? null,
  21. );
  22. const [frame, setFrame] = React.useState<Rect | null>(
  23. initialMetrics?.frame ?? parentFrame ?? null,
  24. );
  25. const onInsetsChange = React.useCallback((event: InsetChangedEvent) => {
  26. setInsets(event.nativeEvent.insets);
  27. setFrame(event.nativeEvent.frame);
  28. }, []);
  29. return (
  30. <NativeSafeAreaView style={styles.fill} onInsetsChange={onInsetsChange}>
  31. {insets != null && frame != null ? (
  32. <SafeAreaFrameContext.Provider value={frame}>
  33. <SafeAreaInsetsContext.Provider value={insets}>
  34. {children}
  35. </SafeAreaInsetsContext.Provider>
  36. </SafeAreaFrameContext.Provider>
  37. ) : null}
  38. </NativeSafeAreaView>
  39. );
  40. }
  41. const styles = StyleSheet.create({
  42. fill: { flex: 1 },
  43. });
  44. function useParentSafeAreaInsets(): EdgeInsets | null {
  45. return React.useContext(SafeAreaInsetsContext);
  46. }
  47. function useParentSafeAreaFrame(): Rect | null {
  48. return React.useContext(SafeAreaFrameContext);
  49. }
  50. export function useSafeAreaInsets(): EdgeInsets {
  51. const safeArea = React.useContext(SafeAreaInsetsContext);
  52. if (safeArea == null) {
  53. throw new Error(
  54. 'No safe area insets value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
  55. );
  56. }
  57. return safeArea;
  58. }
  59. export function useSafeAreaFrame(): Rect {
  60. const frame = React.useContext(SafeAreaFrameContext);
  61. if (frame == null) {
  62. throw new Error(
  63. 'No safe area frame value available. Make sure you are rendering `<SafeAreaProvider>` at the top of your app.',
  64. );
  65. }
  66. return frame;
  67. }
  68. /**
  69. * @deprecated
  70. */
  71. export function useSafeArea(): EdgeInsets {
  72. console.warn('useSafeArea is deprecated, use useSafeAreaInsets instead.');
  73. return useSafeAreaInsets();
  74. }