xref: /expo/docs/pages/_error.tsx (revision cd4bd26b)
1import * as Sentry from '@sentry/browser';
2import React from 'react';
3
4import { getRedirectPath } from '~/common/error-utilities';
5
6const REDIRECT_SUFFIX = '?redirected';
7
8type State = {
9  notFound: boolean;
10  redirectPath?: string;
11  redirectFailed: boolean;
12};
13export default class Error extends React.Component<object, State> {
14  state: State = {
15    notFound: false,
16    redirectPath: undefined,
17    redirectFailed: false,
18  };
19
20  componentDidMount() {
21    this._maybeRedirect();
22  }
23
24  _maybeRedirect = () => {
25    if (typeof window === 'undefined') {
26      return;
27    }
28
29    const { pathname } = window.location;
30
31    if (window.location.search === REDIRECT_SUFFIX) {
32      Sentry.captureMessage(`Redirect failed`);
33      this.setState({ redirectFailed: true });
34      return;
35    }
36
37    const redirectPath = getRedirectPath(pathname);
38
39    if (redirectPath !== pathname) {
40      this.setState({ redirectPath });
41      return;
42    }
43
44    // We are confident now that we can render a not found error
45    this.setState({ notFound: true });
46    Sentry.captureMessage(`Page not found (404)`);
47  };
48
49  componentDidUpdate(prevProps: object, prevState: State) {
50    if (prevState.redirectPath !== this.state.redirectPath && typeof window !== 'undefined') {
51      // Let people actually read the carefully crafted message and absorb the
52      // cool emoji selection, they can just click through if they want speed
53      setTimeout(() => {
54        window.location.href = `${this.state.redirectPath}?redirected`;
55      }, 1200);
56    }
57  }
58
59  render() {
60    return (
61      <div
62        style={{
63          display: 'flex',
64          flex: 1,
65          alignItems: 'center',
66          justifyContent: 'center',
67          height: '100vh',
68          flexDirection: 'column',
69        }}>
70        {this._renderContents()}
71      </div>
72    );
73  }
74
75  _renderContents = () => {
76    const styles = {
77      description: {
78        textAlign: 'center' as const,
79        maxWidth: 450,
80        marginHorizontal: 30,
81        lineHeight: '1.7em',
82      },
83      link: {
84        textAlign: 'center' as const,
85        marginTop: 20,
86      },
87    };
88
89    if (this.state.redirectPath) {
90      return (
91        <>
92          <h1>��️‍♀️️</h1>
93          <p style={styles.description}>
94            Hold tight, we are redirecting you to where we think this URL was intended to take you!
95          </p>
96          <p style={styles.link}>
97            <a id="redirect-link" href={this.state.redirectPath}>
98              Click here to possibly go there more quickly!
99            </a>
100          </p>
101        </>
102      );
103    } else if (this.state.redirectFailed) {
104      return (
105        <>
106          <h1>��️</h1>
107          <p style={styles.description} id="__redirect_failed">
108            We took an educated guess and tried to direct you to the right page, but it seems that
109            did not work out! Maybe it doesn't exist anymore! ��
110          </p>
111          <p style={styles.link}>
112            <a href="/">Go to the Expo documentation, you can try searching there</a>
113          </p>
114        </>
115      );
116    } else if (this.state.notFound) {
117      return (
118        <>
119          <h1>��</h1>
120          <p style={styles.description} id="__not_found">
121            <strong style={{ fontWeight: 'bold' }}>Uh oh, we couldn't find this page!</strong> We've
122            made note of this and will investigate, but it's possible that the page you're looking
123            for no longer exists!
124          </p>
125          <p style={styles.link}>
126            <a href="/">Go to the Expo documentation, you can try searching there</a>
127          </p>
128        </>
129      );
130    } else {
131      // Render nothing statically
132    }
133  };
134}
135