facebook / relay

Relay is a JavaScript framework for building data-driven React applications.
https://relay.dev
MIT License
18.36k stars 1.82k forks source link

Bug? Suspense with usePreloadedQuery doens't get triggered #3300

Open ben-elsen opened 3 years ago

ben-elsen commented 3 years ago

Problem

I have an app in which I show records in the form of cards. I have a browse page, a record detail page and a filter detail page. The record detail page is a subpage of the browse page.

When I click on a card in the browse page, the Suspense isn't tiggered and I don't see anything untill the data is fetched. When I click on a card in the filter detail page and I get redirected to the record detail page, the Suspense works and I see the skelleton while fetching the data.

I use the latest experimental versions of react and relay.

Index.jsx

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { RelayEnvironmentProvider } from 'react-relay/hooks';
import App from './components/App/index';
import ErrorPage from './components/pages/Error';
import relayEnvironment from './relayEnvironment';
import Skelleton from './components/App/Skelleton';
ReactDOM.unstable_createRoot(
  document.getElementById('root'),
).render(
  <React.StrictMode>
    <RelayEnvironmentProvider environment={relayEnvironment}>
      <BrowserRouter>
        <Suspense fallback={<Skelleton />}>
          <Routes>
            <Route path="/">
              <Route path=":subdomain/*" element={<App />} />
              <Route element={<ErrorPage message="Without a subdomain, we cannot find the niche..." />} />
            </Route>
          </Routes>
        </Suspense>
      </BrowserRouter>
    </RelayEnvironmentProvider>
  </React.StrictMode>,
);

App.jsx

import React, { lazy } from 'react';
import { Routes, Route, useParams } from 'react-router-dom';
import { useLazyLoadQuery } from 'react-relay/hooks';
import PreloadRoute from '../common/PreloadRoute';
import { browsePageQuery, appQuery } from '../../graphql/queries';
import GeneralErrorBoundary from '../ErrorBoundaries/GeneralErrorBoundary';
const ErrorPage = lazy(() => import('../pages/Error'));
const Browse = lazy(() => import('../pages/Browse'));
const Search = lazy(() => import('../pages/Search'));
const SignUp = lazy(() => import('../pages/SignUp'));
const SignOut = lazy(() => import('../pages/SignOut'));
const SignIn = lazy(() => import('../pages/SignIn'));
const Skelleton = lazy(() => import('./Skelleton'));
export default function App() {
  /* Params and data */
  const { subdomain } = useParams();
  const data = useLazyLoadQuery(appQuery, { subdomain });
  return (
    <GeneralErrorBoundary>
      <Routes>
        <PreloadRoute
          path="browse/*"
          element={<Browse />}
          query={browsePageQuery}
          variables={{ subdomain }}
          loading={<Skelleton />}
        />
        <Route path="search/*" element={<Search />} />
        <Route path="sign_up/*" element={<SignUp niche={data.niche} />} />
        <Route path="sign_out" element={<SignOut />} />
        <Route path="sign_in" element={<SignIn niche={data.niche} />} />
        <Route element={<ErrorPage message="This page doesn't exist..." />} />
      </Routes>
    </GeneralErrorBoundary>
  );
}

Browse.jsx

/* eslint-disable global-require */
import React, { lazy } from 'react';
import { Flex, Box } from 'rebass';
import { usePreloadedQuery } from 'react-relay/hooks';
import PropTypes from 'prop-types';
import { Routes, useParams } from 'react-router-dom';
import { useFeature } from '@optimizely/react-sdk';
import PreloadRoute from '../../common/PreloadRoute';
import { browsePageQuery, browseRecordPageQuery } from '../../../graphql/queries';
import Logo from '../../common/Logo';
import Tagline from './Tagline';
import Searchfield from '../../common/Searchfield';
import Filters from './Filters';
import RecordLoading from './RecordLoading';
import { PrivateNicheException } from '../../../exceptions';
const Record = lazy(() => import('./Record'));
export default function BrowsePage({ queryReference }) {
  const data = usePreloadedQuery(browsePageQuery, queryReference);
  const [isFilterEnabled] = useFeature('filters', { autoUpdate: true });
  const { subdomain } = useParams();
  const { niche } = data;
  if (!niche.id) {
    throw PrivateNicheException('This is a private niche');
  }
  return (
    <Box
      variant="headingBackground"
      sx={{
        fontFamily: 'body',
        backgroundSize: ['100% 455px'],
        minHeight: 455,
        paddingTop: [20],
      }}
    >
      <Flex variant="container" flexWrap="wrap">
        <Box height={[90]}>
          <Logo />
        </Box>
        <Box width={1} marginTop={[46]} marginLeft="30%" marginRight="30%" textAlign="center">
          <Tagline niche={niche} />
        </Box>
        <Box width={1} marginTop={[90]} marginLeft="30%" marginRight="30%">
          <Searchfield />
        </Box>
        <Box width={1} marginTop={[200]}>
          {isFilterEnabled && <Filters niche={niche} /> }
        </Box>
        <Routes>
          <PreloadRoute
            path="record/:id"
            element={<Record />}
            loading={<RecordLoading />}
            query={browseRecordPageQuery}
            variables={{
              subdomain,
              recordId: window.location.pathname.split('/').slice(-1)[0],
              viewModus: 'detail',
            }}
          />
        </Routes>
      </Flex>
    </Box>
  );
}
BrowsePage.propTypes = {
  queryReference: PropTypes.shape({}).isRequired,
};

Record.jsx

import React, { useEffect } from 'react';
import { usePreloadedQuery } from 'react-relay/hooks';
import PropTypes from 'prop-types';
import { Flex } from 'rebass';
import { browseRecordPageQuery } from '../../../../graphql/queries';
import Card from './Card';
export default function Record({ queryReference }) {
  const data = usePreloadedQuery(browseRecordPageQuery, queryReference);
  useEffect(() => {
    console.log(queryReference);
    console.log(data);
  }, [data, queryReference]);
  /** Make sure the scrolling of the background stops */
  useEffect(() => {
    const { body } = document;
    body.style.height = '100vh';
    body.style.overflowY = 'hidden';
    return () => {
      body.style.height = 'auto';
      body.style.overflowY = 'auto';
    };
  }, []);
  return (
    <Flex
      sx={{
        position: 'fixed',
        display: 'block',
        width: '100%',
        height: '100%',
        top: 0,
        left: 0,
        backgroundColor: 'rgba(0,0,0,0.5)',
      }}
    >
      boehoe
    </Flex>
  );
}
Record.propTypes = {
  queryReference: PropTypes.shape({}),
};
Record.defaultProps = {
  queryReference: null,
};

PreloadRoute.jsx

import React, { useEffect, Suspense, unstable_useTransition as useTransition } from 'react';
import PropTypes from 'prop-types';
import { Route } from 'react-router-dom';
import { useQueryLoader } from 'react-relay/hooks';
import { Loading } from 'front-component-library';

export default function PreloadRoute({
  path, element, query, variables, loading,
}) {
  const [queryRef, loadQuery] = useQueryLoader(query);
  const [startTransition] = useTransition();

  useEffect(() => {
    startTransition(() => {
      loadQuery(variables);
    });
  }, [loadQuery, variables]);

  if (!element) {
    return (
      <Route path={path} />
    );
  }

  return (
    <Route
      path={path}
      element={(
        <Suspense fallback={loading}>
          {
            queryRef != null && React.cloneElement(element, { queryReference: queryRef })
          }
        </Suspense>
      )}
    />
  );
}

PreloadRoute.propTypes = {
  path: PropTypes.string.isRequired,
  element: PropTypes.node,
  query: PropTypes.shape({}),
  variables: PropTypes.shape({}),
  loading: PropTypes.node,
};

PreloadRoute.defaultProps = {
  element: null,
  loading: <Loading />,
  query: () => {},
  variables: {},
};
stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.