QwikDev / qwik

Instant-loading web apps, without effort
https://qwik.dev
MIT License
20.83k stars 1.31k forks source link

[🐞] Using `globalAction$`/`routeAction$` on a `Form` element triggers page reload on submit #4064

Open denisyilmaz opened 1 year ago

denisyilmaz commented 1 year ago

Which component is affected?

Qwik City (routing)

Describe the bug

I have a little "Newsletter Subscribe" Form that has a single email input field. On submit the globalAction$/routeAction$ calls a fetch function (one with GETand one via POST) to subscribe the email to our newsletter list. Locally this is working fine, even with npm run build && npm run deploy with the express adapter. But once deployed to the production server the page reloads on submit. The origin is set in the express/vite.config.ts and i explicitly set reloadDocument to false.

Here is the component:


export type CraftSessionInfo = {
    csrfTokenName: string;
    csrfTokenValue: string;
    isGuest: boolean;
    timeout: number;
};

export type CraftNewsletterSubscribeResponse = {
    email: string;
    message: string;
};

export const useSubscribeToNewsletter = globalAction$(
    async (data, { fail }) => {
        let errorMessage = null;

        const response = await fetch("https://cms.example.com/actions/users/session-info", {
            headers: {
                Accept: "application/json",
            },
        }).catch((error) => (errorMessage = error));
        const craftSession: CraftSessionInfo = await response?.json().catch((error: any) => (errorMessage = error));

        if (craftSession.csrfTokenValue) {
            const response = await fetch("https://cms.example.com/actions/newsletter/subscribe", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    Accept: "application/json",
                    "X-CSRF-Token": craftSession.csrfTokenValue,
                    "X-Requested-With": "XMLHttpRequest",
                },
                body: JSON.stringify({
                    email: data.email,
                }),
            }).catch((error) => (errorMessage = error));

            const craftSubscribe: CraftNewsletterSubscribeResponse = await response?.json().catch((error: any) => (errorMessage = error));

            if (errorMessage) {
                return fail(500, {
                    message: errorMessage,
                });
            }
            return craftSubscribe;
        }
    },
    zod$({
        email: z.string().email(),
    })
);

export default component$<{
    class?: string;
}>((props) => {
    useStylesScoped$(styles);

    const subscribeAction = useSubscribeToNewsletter();
    return (
        <Form action={subscribeAction} reloadDocument={false} spaReset={true}>
            <input type="text" name="email" placeholder="Your email" />
            <button type="submit">Submit</button>
            {subscribeAction.value?.failed && <div>{subscribeAction.value.fieldErrors?.email?.map((email) => email)}</div>}
            {subscribeAction.value?.message && <div>{subscribeAction.value.message}</div>}
        </Form>
    );
});

Reproduction

https://www.ynm.studio/

Steps to reproduce

Click Notify me and add email address. On Submit the page reloads and nothing happens.

System Info

System:
    OS: macOS 13.2.1
    CPU: (10) arm64 Apple M1 Max
    Memory: 1012.45 MB / 64.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.15.0 - ~/.nvm/versions/node/v16.15.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v16.15.0/bin/yarn
    npm: 8.15.1 - ~/.nvm/versions/node/v16.15.0/bin/npm
  Browsers:
    Chrome: 112.0.5615.137
    Edge: 112.0.1722.68
    Safari: 16.3
  npmPackages:
    @builder.io/qwik: ^1.0.0 => 1.0.0 
    @builder.io/qwik-city: ^1.0.0 => 1.0.0 
    undici: 5.22.0 => 5.22.0 
    vite: 4.3.4 => 4.3.4

Additional Information

No response

denisyilmaz commented 1 year ago

Just to make sure I did not make a mistake here. The origin is set in the express/vite.config.ts file:

import { nodeServerAdapter } from '@builder.io/qwik-city/adapters/node-server/vite';
import { extendConfig } from '@builder.io/qwik-city/vite';
import baseConfig from '../../vite.config';

export default extendConfig(baseConfig, () => {
  return {
    build: {
      ssr: true,
      origin: 'https://www.example.com',
      rollupOptions: {
        input: ['src/entry.express.tsx', '@qwik-city-plan'],
      },
    },
    plugins: [nodeServerAdapter({
      name: 'express',
      ssg: {
        include: [],
        origin: 'https://www.example.com',
      }
    })],
  };
});

and not as mentioned in #3642 via the a environment variable directly.

manucorporat commented 1 year ago

i can see it working

denisyilmaz commented 1 year ago

Sorry, i forgot to update the issue. Actually the env ORIGIN was required on the node express server additionally to the settings on the vite config file. With this my globalAction$ was working. Changing it to a routeAction$ without any other change is causing a reload still. I'm trying to find a pattern here and will get back if this persists. Also: the action is transmitting a CSRF token to the backend which does not get validated. The token is generated by requesting it in the same action through a GET request to the server. I don't understand why it's not validated but this might be a config error with the backend though… Just posting this here as this might give insights were my setup is flawed.