Search code examples
reactjsformsactiononchangeremix

Remix submit form onChange and get live update from useActionData()


I am trying to create a remix page where i want to get live preview when i change a Select tag. It is going through a Form since i want it to translate the data.

A solution to my problem could be to make preventDefault(first code example) to work or to find a way to make useActionData()(second code example) to work when using useFetcher

With this code, the preventDefault does not work. I need preventDefault to work so that the selector doesn't reset. I could make the action function also send the name so that the DefaultValue in select is imported from action function, but this is going to become a big form and i dont want to refresh the whole page.

export async function action({ request }: ActionArgs) {
    const form = new URLSearchParams(await request.text())
    const name = form.get('name');
    console.log('name', name);
    return json({ message: name ? `Hello ${name}!` : 'Select name' });
}

export default function Page() {
    const actionData = useActionData();
    console.log('actionData', actionData);

    return (
        <>
            <Form method="post">

                <h1>{actionData ? actionData.message : 'Select name.'}</h1>

                <select 
                    name="name" 
                    onChange={e => {
                        e.preventDefault();
                        e.target.form.submit();
                    }}
                >
                    <option value="name 1">Name 1</option>
                    <option value="name 2">Name 2</option>
                    <option value="name 3">Name 3</option>
                </select>

                <button type="submit" className="bg-blue-500 text-white font-bold py-2 px-4">
                    Submit
                </button>

                </Form>
        </>
    );
}

I have also tried following https://github.com/remix-run/remix/issues/1807 using useRef and useFetcher, but even tho the action function runs, it gives me undefined when logging actionData.

export async function action({ request }: ActionArgs) {
    const form = new URLSearchParams(await request.text())
    const name = form.get('name');
    console.log('name', name);
    return json({ message: name ? `Hello ${name}!` : 'Select name' });
}

export default function Page() {
    const actionData = useActionData();
    console.log('actionData', actionData); // Gives undefined

    const formRef = useRef<HTMLFormElement>(null);
    const fetcher = useFetcher();

    return (
        <>
            <fetcher.Form method="post" ref={formRef}>

                <h1>{actionData ? actionData.message : 'Select name.'}</h1>

                <select 
                    name="name" 
                    onChange={() => {
                        fetcher.submit(formRef.current);
                    }}
                >
                    <option value="name 1">Name 1</option>
                    <option value="name 2">Name 2</option>
                    <option value="name 3">Name 3</option>
                </select>

                <button type="submit" className="bg-blue-500 text-white font-bold py-2 px-4">
                    Submit
                </button>

            </fetcher.Form >
        </>
    );
}

Solution

  • I found a solution, it was to use fetcher.load, and then have it as a own loader file.

    Main file test.tsx:

    import { useFetcher } from '@remix-run/react';
    
    export default function Page() {
        const fetcher = useFetcher();
        return (
            <>
                <div>
                    <h1>{fetcher.data ? fetcher.data.message : 'Select name.'}</h1>
    
                    <select
                        name="name"
                        onChange={e => {
                            fetcher.load(`loader/?name=${e.target.value}`);
                        }} 
                    >
                        <option value="name 1">Name 1</option>
                        <option value="name 2">Name 2</option>
                        <option value="name 3">Name 3</option>
                    </select>
    
                    <button type="submit" className="bg-blue-500 text-white font-bold py-2 px-4">
                        Submit
                    </button>
                </div>
            </>
        );
    }
    

    Loaderfile loader.ts:

    import type { LoaderArgs } from '@remix-run/node';
    import { json } from '@remix-run/node';
    
    export async function loader({ request }: LoaderArgs) {
        const form = new URLSearchParams(await request.text());
        const name = form.get('name');
        return json({ message: name ? `Hello ${name}!` : 'Select name' });
    }