How do I redirect user to another page on Embedded App Admin Page?

awidget
Shopify Partner
6 0 0

It's frustrating I can't make such simple thing as redirecting user to another app page after form submitting. 
I'm using recommended Remix cli template. I tried different approaches but they all fail.


Here is my app/routes/_index/route.jsx

 

import { useState } from "react";
import { useActionData } from "@remix-run/react";
import { authenticate } from "../../shopify.server";

import {
  AppProvider,
  Button,
  Text, ...
} from "@shopify/polaris";

export default function App() {
  ...
  const handleSubmit = async (event) => {
    event.preventDefault();
    setUiState('saving');
    // handling data ...

    here i need to redirect user to some page inside the app. let's say Test page.  How?
    //open('/app/test', '_self'); 
    //navigate('/app/test');
// const { redirect } = await authenticate.admin(request);
// return redirect('/app/test');

 

I'm getting different errors but none of the attempts was successful.

- with redirect()
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'admin')

- with open()
xxx.trycloudflare.com redirected you too many times.

- with const navigate = useNavigate();

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component.

 

So what's the right method for this? Can you help please?

 

Replies 3 (3)

ncataland
Shopify Partner
2 0 1

Hi there. Shopify encourages using the Admin redirect helper for embedded apps. However, this helper must be called from within a Remix loader or action function. See below for usage examples. 

From the error output you provided, it looks like the admin context isn't loading correctly. Try changing the authenticate import path from "../../shopify.server" to "../shopify.server". Also, you might want to try moving your application logic from the app/routes/_index/route.jsx file to the app/routes/app._index.jsx file, as this is the main app landing page (though this may vary depending on the type of application you are developing.)

 

 

Usage examples

 

Use loader if you want the user to be redirected before page is loaded.

 

import { authenticate } from "../shopify.server";

export const loader = async ({ request }) => {
 	const { session, redirect } = await authenticate.admin(request);

	// App logic, if needed

 	return redirect("/app/other_page");
};

 

 

Use action if you want to collect data from the user, then redirect on submit.

 

import { Form, useActionData, useSubmit } from "@remix-run/react";
import { authenticate } from "../shopify.server";

export const action = async ({ request }) => {
 	const { session, redirect } = await authenticate.admin(request);

	// Await user form submission data
	const formData = await request.formData();
	
	// Do stuff with the data

 	return redirect("/app/other_page");
};

export default function Index() {

	const actionData = useActionData();
	const submit = useSubmit();
	const handleSave = () => submit(formData, { method: "POST" });

	return (
		<Form>
			<TextField value={userdata} label="Enter your data" type="text" name="userdata" />
			<Button variant="primary" onclick={handleSave} >Save</Button>
		</Form>
	)
}	

 

 

awidget
Shopify Partner
6 0 0

Thanks for you reply! But I still can't get it working. 

I used your code and updated it a little to avoid raising errors. 
However when I click the button nothing happens, no "start action" in console, no errors. What's wrong? Why action is not even triggered?

Here is my current code for this very basic page:

import { Form, redirect, useActionData, useSubmit } from "@remix-run/react";
import { authenticate } from "../../shopify.server";
import { useState } from "react";

import {
    AppProvider,
    TextField,
    Button,
  } from "@shopify/polaris";

export const action = async ({ request }) => {
  console.log("start action");

 	const { session, redirect } = await authenticate.admin(request);

	// Await user form submission data
	const formData = await request.formData();
	
	// Do stuff with the data

 	return redirect("/app/instructions");
};

export default function Index() {
  const [username, setUsername] = useState("");
	const formData = useActionData();
	const submit = useSubmit();
  const handleSave = () => submit(formData, { method: "POST" });

  const i18n = {
      Polaris: {
        ResourceList: {
        },
      },
    };
      
  const setUserdata = (value) => {
    console.log(value);
    setUsername(value);
  }

	return (
		<AppProvider i18n={i18n}>
        <Form  method="post">
            <TextField value={username} 
              onChange={setUserdata}
              label="Enter your data" type="text" name="username" />
            <Button variant="primary" onClick={handleSave} >Save</Button>
        </Form>
    </AppProvider>
	)
}	




BrainStation23
Shopify Partner
117 26 27

Hi @awidget ,

I see you're trying to submit a formData using the action. But the formData variable is out of scope for handleSave function. Since you're already using a state and a useSubmit to programmatically submit the form, I suggest instead of trying to use the formData, you use the username state itself. 

That way you can make sure that the post request has a body. By default, if you don't give any value or undefined as the request body, the remix will automatically redirect the request to the loader function instead of the action function. I've modified you're handleSave function code to run the action function as you submit the form.

 

 

  const handleSave = () => submit({ username }, { method: "POST" });

 


The result is community remix.PNG

 

Kazi Muktadir Ahmed | Brain Station 23
 - Was my reply helpful? Click Like to let me know! 
 - Was your question answered? Mark it as an Accepted Solution

Brain Station 23 PLC
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
Email: js.team@brainstation-23.com