Shopify / shopify-app-js

MIT License
284 stars 112 forks source link

[shopify-app-remix] Support redirects thrown from authenticated routes #374

Open brendanrygus opened 1 year ago

brendanrygus commented 1 year ago

Hi team, thanks for all the hard work to create this Remix starter. I ran into an issue when trying to use Remix's redirects from the route loaders. I can confirm that the issue does not occur when the route has no Shopify authentication in the loader.

Issue summary

Experiencing errors and infinite loop when throwing a Remix redirect from a route authenticated by Shopify App Remix.

Flow:

Expected behavior

Actual behavior

Logs were too long to include but I am happy to pair if you are unable to reproduce.

Steps to reproduce the problem

  1. Create two routes in a Remix app (A, B)
  2. In A route loader, authenticate the request with await shopify.authenticate.admin(request)
  3. In A route loader, throw a redirect to route B
  4. Load the app to route A
jriemann commented 1 year ago

+1 encountering this issue as well. I am trying to implement a paywall page that all subscription-less merchants get automatically redirected to, and this is blocking progress. I imagine any app devs that want to have subscription-based billing will want something similar. Thanks!

shilomagen commented 1 year ago

+1 as well. I'm trying to redirect to a child route /app/onboarding from /app and with shopify.authenticate.admin(request) I get infinite loop of redirections.

btomaj commented 1 year ago

+1 I was able to reproduce this with a barebones implementation:

Route A app/routes/app.test.jsx

import { redirect } from "@remix-run/node";                                      
import { authenticate } from "../shopify.server";                                

export async function loader({ request }) {                                      
    await authenticate.admin(request);                                           
    throw redirect("/app/error");                                                
}    

Route B app/routes/app.error.jsx

export default function () {}
btomaj commented 1 year ago

This issue only occurs if Route A is the first page loaded, is refreshed, or is accessed directly via URL.

btomaj commented 1 year ago

It looks like the redirect causes a race condition that causes an infinite loop because Remix tries to reload the page.

[Debug] [bugsnag] – "Loaded!" (vendors-node_modules_bugsnag_js_browser_notifier_js-node_modules_bugsnag_plugin-react_dist_bu-4c8368-ca7ba3259abea3d0334c688f1ae481f899f0cb6d844bd1049a20086c67650d81.js, line 2)
[Error] Unable to post message to https://forms.shopifyapps.com. Recipient has origin https://baseline-afternoon-nature-crop.trycloudflare.com.

    postMessage
    (anonymous function) (vendors-node_modules_shopify_app-bridge-host_index_js-a2a54210984567e5.js:1:1762)
[Error] Initial URL (/app/error) does not match URL at time of hydration (/app/test), reloading page...
    RemixBrowser (chunk-K3SBKXTE.js:7705)
    renderWithHooks (chunk-GIAAE3CH.js:12171)
    mountIndeterminateComponent (chunk-GIAAE3CH.js:14921)
    beginWork$1 (chunk-GIAAE3CH.js:19749)
    performUnitOfWork (chunk-GIAAE3CH.js:19194)
    workLoopConcurrent (chunk-GIAAE3CH.js:19185)
    renderRootConcurrent (chunk-GIAAE3CH.js:19160)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18674)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: undefined is not an object (evaluating 'module.links')
    (anonymous function) (chunk-K3SBKXTE.js:5796)
    map
    getLinksForMatches (chunk-K3SBKXTE.js:5793)
    mountMemo (chunk-GIAAE3CH.js:12817)
    useMemo (chunk-GIAAE3CH.js:13141)
    Links (chunk-K3SBKXTE.js:6218)
    renderWithHooks (chunk-GIAAE3CH.js:12171)
    mountIndeterminateComponent (chunk-GIAAE3CH.js:14921)
    callCallback2 (chunk-GIAAE3CH.js:3674)
    dispatchEvent
    invokeGuardedCallbackDev (chunk-GIAAE3CH.js:3699)
    invokeGuardedCallback (chunk-GIAAE3CH.js:3733)
    beginWork$1 (chunk-GIAAE3CH.js:19761)
    performUnitOfWork (chunk-GIAAE3CH.js:19194)
    workLoopConcurrent (chunk-GIAAE3CH.js:19185)
    renderRootConcurrent (chunk-GIAAE3CH.js:19160)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18674)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.
    printWarning (chunk-GIAAE3CH.js:521)
    error (chunk-GIAAE3CH.js:505)
    errorHydratingContainer (chunk-GIAAE3CH.js:8769)
    recoverFromConcurrentError (chunk-GIAAE3CH.js:18729)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18680)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: undefined is not an object (evaluating 'module.links')
    (anonymous function) (chunk-K3SBKXTE.js:5796)
    map
    getLinksForMatches (chunk-K3SBKXTE.js:5793)
    mountMemo (chunk-GIAAE3CH.js:12817)
    useMemo (chunk-GIAAE3CH.js:13141)
    Links (chunk-K3SBKXTE.js:6218)
    renderWithHooks (chunk-GIAAE3CH.js:12171)
    mountIndeterminateComponent (chunk-GIAAE3CH.js:14921)
    callCallback2 (chunk-GIAAE3CH.js:3674)
    dispatchEvent
    invokeGuardedCallbackDev (chunk-GIAAE3CH.js:3699)
    invokeGuardedCallback (chunk-GIAAE3CH.js:3733)
    beginWork$1 (chunk-GIAAE3CH.js:19761)
    performUnitOfWork (chunk-GIAAE3CH.js:19194)
    workLoopSync (chunk-GIAAE3CH.js:19133)
    renderRootSync (chunk-GIAAE3CH.js:19112)
    recoverFromConcurrentError (chunk-GIAAE3CH.js:18732)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18680)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: Right side of assignment cannot be destructured
    RemixRoute (chunk-K3SBKXTE.js:6048)
    renderWithHooks (chunk-GIAAE3CH.js:12171)
    mountIndeterminateComponent (chunk-GIAAE3CH.js:14921)
    callCallback2 (chunk-GIAAE3CH.js:3674)
    dispatchEvent
    invokeGuardedCallbackDev (chunk-GIAAE3CH.js:3699)
    invokeGuardedCallback (chunk-GIAAE3CH.js:3733)
    beginWork$1 (chunk-GIAAE3CH.js:19761)
    performUnitOfWork (chunk-GIAAE3CH.js:19194)
    workLoopSync (chunk-GIAAE3CH.js:19133)
    renderRootSync (chunk-GIAAE3CH.js:19112)
    recoverFromConcurrentError (chunk-GIAAE3CH.js:18732)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18680)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: Right side of assignment cannot be destructured
    errorBoundary (app-EB2ZDQOO.js:15888)
    renderWithHooks (chunk-GIAAE3CH.js:12171)
    mountIndeterminateComponent (chunk-GIAAE3CH.js:14921)
    callCallback2 (chunk-GIAAE3CH.js:3674)
    dispatchEvent
    invokeGuardedCallbackDev (chunk-GIAAE3CH.js:3699)
    invokeGuardedCallback (chunk-GIAAE3CH.js:3733)
    beginWork$1 (chunk-GIAAE3CH.js:19761)
    performUnitOfWork (chunk-GIAAE3CH.js:19194)
    workLoopSync (chunk-GIAAE3CH.js:19133)
    renderRootSync (chunk-GIAAE3CH.js:19112)
    recoverFromConcurrentError (chunk-GIAAE3CH.js:18732)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18680)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: undefined is not an object (evaluating 'routeModules[match.id].handle')
    (anonymous function) (chunk-K3SBKXTE.js:6731)
    map
    mountMemo (chunk-GIAAE3CH.js:12817)
    useMemo (chunk-GIAAE3CH.js:13141)
    ScrollRestoration2 (chunk-K3SBKXTE.js:7756)
    renderWithHooks (chunk-GIAAE3CH.js:12171)
    mountIndeterminateComponent (chunk-GIAAE3CH.js:14921)
    callCallback2 (chunk-GIAAE3CH.js:3674)
    dispatchEvent
    invokeGuardedCallbackDev (chunk-GIAAE3CH.js:3699)
    invokeGuardedCallback (chunk-GIAAE3CH.js:3733)
    beginWork$1 (chunk-GIAAE3CH.js:19761)
    performUnitOfWork (chunk-GIAAE3CH.js:19194)
    workLoopSync (chunk-GIAAE3CH.js:19133)
    renderRootSync (chunk-GIAAE3CH.js:19112)
    recoverFromConcurrentError (chunk-GIAAE3CH.js:18732)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18680)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] The above error occurred in the <Links> component:

Links@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:6212:22
head
html
App
RemixRoute@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:6037:5
RenderedRoute@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3658:11
RenderErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4239:14
DataRoutes@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3955:12
Router@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4038:12
RouterProvider@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3905:11
RemixErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:5625:10
RemixBrowser@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:7712:48

React will try to recreate this component tree from scratch using the error boundary you provided, RenderErrorBoundary.
    logCapturedError (chunk-GIAAE3CH.js:14036)
    callback (chunk-GIAAE3CH.js:14082)
    callCallback (chunk-GIAAE3CH.js:10511)
    commitUpdateQueue (chunk-GIAAE3CH.js:10528)
    commitLayoutEffectOnFiber (chunk-GIAAE3CH.js:17065)
    commitLayoutMountEffects_complete (chunk-GIAAE3CH.js:17976)
    commitLayoutEffects_begin (chunk-GIAAE3CH.js:17965)
    commitLayoutEffects (chunk-GIAAE3CH.js:17916)
    commitRootImpl (chunk-GIAAE3CH.js:19349)
    commitRoot (chunk-GIAAE3CH.js:19273)
    finishConcurrentRender (chunk-GIAAE3CH.js:18756)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18714)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] React Router caught the following error during render (2)
TypeError: undefined is not an object (evaluating 'module.links') — links.js:29
{componentStack: "↵Links@https://baseline-afternoon-nature-crop.tryc…flare.com/build/_shared/chunk-K3SBKXTE.js:7712:48"}
    componentDidCatch (chunk-K3SBKXTE.js:4266)
    callback (chunk-GIAAE3CH.js:14088)
    callCallback (chunk-GIAAE3CH.js:10511)
    commitUpdateQueue (chunk-GIAAE3CH.js:10528)
    commitLayoutEffectOnFiber (chunk-GIAAE3CH.js:17065)
    commitLayoutMountEffects_complete (chunk-GIAAE3CH.js:17976)
    commitLayoutEffects_begin (chunk-GIAAE3CH.js:17965)
    commitLayoutEffects (chunk-GIAAE3CH.js:17916)
    commitRootImpl (chunk-GIAAE3CH.js:19349)
    commitRoot (chunk-GIAAE3CH.js:19273)
    finishConcurrentRender (chunk-GIAAE3CH.js:18756)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18714)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] The above error occurred in the <ErrorBoundary> component:

ErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/routes/app-EB2ZDQOO.js:16153:63
RemixRouteError@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:6057:5
RenderErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4239:14
Outlet@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4025:25
body
html
App
RemixRoute@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:6037:5
RenderedRoute@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3658:11
RenderErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4239:14
DataRoutes@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3955:12
Router@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4038:12
RouterProvider@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3905:11
RemixErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:5625:10
RemixBrowser@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:7712:48

React will try to recreate this component tree from scratch using the error boundary you provided, RenderErrorBoundary.
    logCapturedError (chunk-GIAAE3CH.js:14036)
    callback (chunk-GIAAE3CH.js:14082)
    callCallback (chunk-GIAAE3CH.js:10511)
    commitUpdateQueue (chunk-GIAAE3CH.js:10528)
    commitLayoutEffectOnFiber (chunk-GIAAE3CH.js:17065)
    commitLayoutMountEffects_complete (chunk-GIAAE3CH.js:17976)
    commitLayoutEffects_begin (chunk-GIAAE3CH.js:17965)
    commitLayoutEffects (chunk-GIAAE3CH.js:17916)
    commitRootImpl (chunk-GIAAE3CH.js:19349)
    commitRoot (chunk-GIAAE3CH.js:19273)
    finishConcurrentRender (chunk-GIAAE3CH.js:18756)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18714)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] React Router caught the following error during render (2)
TypeError: Right side of assignment cannot be destructured — components.js:58
{componentStack: "↵ErrorBoundary@https://baseline-afternoon-nature-c…flare.com/build/_shared/chunk-K3SBKXTE.js:7712:48"}
    componentDidCatch (chunk-K3SBKXTE.js:4266)
    callback (chunk-GIAAE3CH.js:14088)
    callCallback (chunk-GIAAE3CH.js:10511)
    commitUpdateQueue (chunk-GIAAE3CH.js:10528)
    commitLayoutEffectOnFiber (chunk-GIAAE3CH.js:17065)
    commitLayoutMountEffects_complete (chunk-GIAAE3CH.js:17976)
    commitLayoutEffects_begin (chunk-GIAAE3CH.js:17965)
    commitLayoutEffects (chunk-GIAAE3CH.js:17916)
    commitRootImpl (chunk-GIAAE3CH.js:19349)
    commitRoot (chunk-GIAAE3CH.js:19273)
    finishConcurrentRender (chunk-GIAAE3CH.js:18756)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18714)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] The above error occurred in the <ScrollRestoration2> component:

ScrollRestoration2@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:7752:9
body
html
App
RemixRoute@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:6037:5
RenderedRoute@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3658:11
RenderErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4239:14
DataRoutes@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3955:12
Router@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:4038:12
RouterProvider@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:3905:11
RemixErrorBoundary@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:5625:10
RemixBrowser@https://baseline-afternoon-nature-crop.trycloudflare.com/build/_shared/chunk-K3SBKXTE.js:7712:48

React will try to recreate this component tree from scratch using the error boundary you provided, RenderErrorBoundary.
    logCapturedError (chunk-GIAAE3CH.js:14036)
    callback (chunk-GIAAE3CH.js:14082)
    callCallback (chunk-GIAAE3CH.js:10511)
    commitUpdateQueue (chunk-GIAAE3CH.js:10528)
    commitLayoutEffectOnFiber (chunk-GIAAE3CH.js:17065)
    commitLayoutMountEffects_complete (chunk-GIAAE3CH.js:17976)
    commitLayoutEffects_begin (chunk-GIAAE3CH.js:17965)
    commitLayoutEffects (chunk-GIAAE3CH.js:17916)
    commitRootImpl (chunk-GIAAE3CH.js:19349)
    commitRoot (chunk-GIAAE3CH.js:19273)
    finishConcurrentRender (chunk-GIAAE3CH.js:18756)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18714)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] React Router caught the following error during render (2)
TypeError: undefined is not an object (evaluating 'routeModules[match.id].handle') — components.js:860
{componentStack: "↵ScrollRestoration2@https://baseline-afternoon-nat…flare.com/build/_shared/chunk-K3SBKXTE.js:7712:48"}
    componentDidCatch (chunk-K3SBKXTE.js:4266)
    callback (chunk-GIAAE3CH.js:14088)
    callCallback (chunk-GIAAE3CH.js:10511)
    commitUpdateQueue (chunk-GIAAE3CH.js:10528)
    commitLayoutEffectOnFiber (chunk-GIAAE3CH.js:17065)
    commitLayoutMountEffects_complete (chunk-GIAAE3CH.js:17976)
    commitLayoutEffects_begin (chunk-GIAAE3CH.js:17965)
    commitLayoutEffects (chunk-GIAAE3CH.js:17916)
    commitRootImpl (chunk-GIAAE3CH.js:19349)
    commitRoot (chunk-GIAAE3CH.js:19273)
    finishConcurrentRender (chunk-GIAAE3CH.js:18756)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18714)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
    reportError (chunk-GIAAE3CH.js:14755)
    commitRootImpl (chunk-GIAAE3CH.js:19399)
    commitRoot (chunk-GIAAE3CH.js:19273)
    finishConcurrentRender (chunk-GIAAE3CH.js:18756)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18714)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: undefined is not an object (evaluating 'routeModules[match.id].handle') — components.js:860
    (anonymous function) (chunk-K3SBKXTE.js:5662)
    commitHookEffectListMount (chunk-GIAAE3CH.js:16904)
    commitPassiveMountOnFiber (chunk-GIAAE3CH.js:18152)
    commitPassiveMountEffects_complete (chunk-GIAAE3CH.js:18125)
    commitPassiveMountEffects_begin (chunk-GIAAE3CH.js:18115)
    commitPassiveMountEffects (chunk-GIAAE3CH.js:18105)
    flushPassiveEffectsImpl (chunk-GIAAE3CH.js:19486)
    flushPassiveEffects (chunk-GIAAE3CH.js:19443)
    (anonymous function) (chunk-GIAAE3CH.js:19324)
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: undefined is not an object (evaluating 'routeModules[match.id].handle') — components.js:860
    (anonymous function) (chunk-K3SBKXTE.js:5662)
    commitHookEffectListMount (chunk-GIAAE3CH.js:16904)
    invokePassiveEffectMountInDEV (chunk-GIAAE3CH.js:18320)
    invokeEffectsInDev (chunk-GIAAE3CH.js:19697)
    commitDoubleInvokeEffectsInDEV (chunk-GIAAE3CH.js:19682)
    flushPassiveEffectsImpl (chunk-GIAAE3CH.js:19499)
    flushPassiveEffects (chunk-GIAAE3CH.js:19443)
    (anonymous function) (chunk-GIAAE3CH.js:19324)
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)
[Error] TypeError: null is not an object (evaluating 'window.history.state.key') — test:10
    (anonymous function) (test:15)
    Global Code (test:18)
    write
    (anonymous function) (app-bridge.js:1:21755)
[Error] Initial URL (/app/error) does not match URL at time of hydration (/app/test), reloading page...
    RemixBrowser (chunk-K3SBKXTE.js:7705)
    renderWithHooks (chunk-GIAAE3CH.js:12171)
    mountIndeterminateComponent (chunk-GIAAE3CH.js:14921)
    beginWork$1 (chunk-GIAAE3CH.js:19749)
    performUnitOfWork (chunk-GIAAE3CH.js:19194)
    workLoopConcurrent (chunk-GIAAE3CH.js:19185)
    renderRootConcurrent (chunk-GIAAE3CH.js:19160)
    performConcurrentWorkOnRoot (chunk-GIAAE3CH.js:18674)
    performConcurrentWorkOnRoot
    workLoop (chunk-GIAAE3CH.js:197)
    flushWork (chunk-GIAAE3CH.js:176)
    performWorkUntilDeadline (chunk-GIAAE3CH.js:384)

The logs repeat from here.

brendanrygus commented 1 year ago

@btomaj After upgrading to the latest version to get the redirect method from the package, I was able to solve this by bringing the boundary helper from the package into my Error Boundary components.

TLDR; the package throws a certain shape of redirect response when it needs to redirect, and hands off control to Shopify App Bridge to actually navigate the window to the new page. Without handling from the package boundary.error helper, Remix doesn't know how to properly handle the response.

I had to add this logic to every route with a granular error boundary, or in a single shared app error boundary.

Here's my specific example. The implementation of BugsnagErrorBoundary isn't really important - it could be any custom error UI for your page:

import type {ComponentProps} from "react";
import {useRouteError} from "@remix-run/react";
import {boundary} from "@shopify/shopify-app-remix";

import {BugsnagErrorBoundary, type ErrorProps} from "./BugsnagErrorBoundary";

export const EmbeddedAppErrorBoundary = (props: ErrorProps) => {
  const error = useRouteError();
  try {
    // Catch redirect Response thrown by @shopify/shopify-app-remix
    const shopifyError = boundary.error(error);
    return shopifyError;
  } catch {
    // Re-throw all other errors to Error Boundary
    return <BugsnagErrorBoundary {...props} />;
  }
};

Usage:

// /routes/some.page.tsx

export const ErrorBoundary = () => <EmbeddedAppErrorBoundary fallback={<p>Oopsie</p>} />

Hopefully the documentation can be updated with more details about customizing error boundaries soon.

btomaj commented 1 year ago

@brendanrygus thank you for explaining. It makes perfect sense now. https://github.com/Shopify/shopify-app-js/blob/404b10eb1d2babb12d11c6d70ed6e8f2ec9f5f7b/packages/shopify-app-remix/src/server/boundary/error.tsx can throw an error inside an error boundary. That seems unintended? Do you know why it's been designed that way?

Update: Hmm, I can't seem to get that to work. I tried two things, and neither of them fixed the issue.

I tried modifying the default app/route/app.jsx file, but that didn't work.

export function ErrorBoundary() {
    const error = useRouteError();
    try {
        const boundaryError = boundary.error(error);
        return boundaryError;
    } catch {
        return error; // I also tried commenting out the return
    }
}

I tried putting this in Route A app/routes/app.test.jsx, but this didn't work either.

export function ErrorBoundary() {
    const error = useRouteError();
    try {
        const boundaryError = boundary.error(error);
        return boundaryError;
    } catch {
        return ( // I also tried return error; and commenting it out all together
            <div>error</div>
        );
    }
}
ghost commented 1 year ago

Yo any updates on this on how you guys handled it? Facing the same problem here

ghost commented 1 year ago

Ah I got it working. It was access_scopes all along for my part. I was using yarn config:link before yarn config:push. I got it working by writing the same access scopes both on shopify.app.toml and .env and used yarn config:push without the config:link xd.

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days.

btomaj commented 1 year ago

For anyone who faces this problem: you can’t use the Remix redirect function. Shopify provide a redirect helper through const { redirect } = await authenticate.admin(request); that you should be using instead.

vinhtranchau commented 10 months ago

@btomaj I want to use the redirect helper, but the RedirectTarget can be only "_self", "_top" and "_parent". I want to redirect with "_blank". Is there any way?

btomaj commented 8 months ago

@btomaj I want to use the redirect helper, but the RedirectTarget can be only "_self", "_top" and "_parent". I want to redirect with "_blank". Is there any way?

Using _blank doesn't make sense in the context of Shopify because your app loads inside their admin environment. I can't remember how to do it, but I think there's a way to do what you are looking for using AppBridge if you dig around the documentation (or Google).

github-actions[bot] commented 6 months ago

We're labeling this issue as stale because there hasn't been any activity on it for 60 days. While the issue will stay open and we hope to resolve it, this helps us prioritize community requests.

You can add a comment to remove the label if it's still relevant, and we can re-evaluate it.

huykon commented 3 weeks ago

still get the same issue although I use redirect exported from await authenticate.admin(request);