Reputation: 1018
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
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