JuCachalot
JuCachalot

Reputation: 1018

Fetcher triggers a full page reload with React Router v7 (framework)

I use React Router v7 as a framework. I don't understand why posting data with a fetcher triggers a full page reload. I might be mistaken but to my understanding, it should just post the data, reload the route data and re-render the components. What actually happens is: it posts the data, it gets the new data and then a full page reload is triggered. It looks like the fetcher does not prevent the browser to post the form again in the end.

Here is the code:


// This is the component set for the route "test-fetcher"
import { initialData, saveData } from 'data/data-server'; // this just read and write data from a JSON file server side
import type { Route } from './+types/test-fetcher';
import SideBar from '~/components/SideBar';
import BasicDisplay from '~/components/BasicDisplay';

export async function loader({ params }: Route.LoaderArgs) {
    return initialData();
}

export async function action({ params, request }: Route.LoaderArgs) {
    const data = await request.formData();

    await saveData(data.get('label'));

    return Response.json({ ok: true });
}

export default function TestFetcher({
    loaderData,
}: Route.ComponentProps) {
    const { elts } = loaderData;

    return (
        <div>
            <BasicDisplay elts={elts} />
            <SideBar />
        </div>
    );
}
// SideBar component who uses the useFetcher hook
import { useState } from 'react';
import { useFetcher } from 'react-router';

export default function SideBar() {
    const [inputValue, setInputValue] = useState('post me please');
    const fetcher = useFetcher();
    const onInputChange = (evt) => {
        setInputValue(evt.currentTarget.value);
    };

    return (
        <aside className="w-2/5">
            <fetcher.Form
                method="post"
                action="/test-fetcher"
                className="my-10 px-4"
            >
                <input
                    type="text"
                    value={inputValue}
                    onChange={onInputChange}
                    name="label"
                />
                <button type="submit">Submit</button>
            </fetcher.Form>
        </aside>
    );
}

// Basic display component
const BasicDisplay = ({ elts }) => {
    return (
        <ul>
            {elts.map((e) => (
                <li key={e.id}>{e.label}</li>
            ))}
        </ul>
    );
};

export default BasicDisplay;

// package.json

{
    [...]
    "private": true,
    "type": "module",
    "scripts": {
        "build": "react-router build",
        "dev": "react-router dev",
        "start": "react-router-serve ./build/server/index.js",
        "typecheck": "react-router typegen && tsc"
    },
    "dependencies": {
        "@dagrejs/dagre": "^1.1.4",
        "@hookform/resolvers": "^4.1.2",
        "@radix-ui/react-label": "^2.1.2",
        "@radix-ui/react-slot": "^1.1.2",
        "@react-router/node": "^7.1.5",
        "@react-router/serve": "^7.1.5",
        "@xyflow/react": "^12.4.2",
        "class-variance-authority": "^0.7.1",
        "clsx": "^2.1.1",
        "isbot": "^5.1.17",
        "lucide-react": "^0.474.0",
        "openai": "^4.85.2",
        "react": "^19.0.0",
        "react-dom": "^19.0.0",
        "react-hook-form": "^7.54.2",
        "react-router": "^7.1.5",
        "tailwind-merge": "^3.0.1",
        "tailwindcss-animate": "^1.0.7",
        "zod": "^3.24.2",
        "zustand": "^5.0.3"
    },
    "devDependencies": {
        "@react-router/dev": "^7.1.5",
        "@tailwindcss/vite": "^4.0.0",
        "@types/node": "^20",
        "@types/react": "^19.0.1",
        "@types/react-dom": "^19.0.1",
        "autoprefixer": "^10.4.20",
        "react-router-devtools": "^1.0.0",
        "tailwindcss": "^4.0.4",
        "typescript": "^5.7.2",
        "vite": "^5.4.11",
        "vite-tsconfig-paths": "^5.1.4"
    }
}

Upvotes: -1

Views: 28

Answers (1)

JuCachalot
JuCachalot

Reputation: 1018

The issue in the code above was in the non provided data-server module. In this module, I imported a function from the node fs/promise API but that's not the way React Router / Remix is supposed to work, at least not with the default template because it's not built on node API but on the Web Fetch API so that it can work in any JS runtime (node and others). React Router / Remix the uses adapters to make your code on different platforms.

Upvotes: 0

Related Questions