[BUG] useForm from Refine React hook form package is not detecting current route
Describe the bug
useForm from @refinedev/react-hook-form is not detecting current route, I have to manually add the resource route for it to work
Steps To Reproduce
- create a new refine nextjs app with
npm create refine-app@latest -- -o refine-nextjs my-refine-nextjs-app - create a new resource
{ name: "workspaces", list: "/workspaces", create: "/workspaces/create", meta: { canDelete: true, }, }, - create a new page in pages
workspaces/create/index.tsx - create few input fields and validate using
zodand@refinedev/react-hook-form(I think you can skip the zod part) - try saving by pressing the submit button, it currently use the resource name instead of the create resource
Expected behavior
From the documentation, I expect it to detect the current route URL and use the appropriate resource without having to manually include it
Screenshot
Desktop
- OS: [macbook]
- Browser [chrome]
- Version [121.0.6167.139]
- Used dataProvider [@refinedev/simple-rest]
Mobile
No response
Additional Context
No response
Hey @Cavdy thanks for the report! We'll check it and get back to you.
Hello @Cavdy could you give us a minimal reproducible example? While the report details are good, we can't easily understand the problem without the code.
Hello @Cavdy,
After @BatuhanW, I tried too to reproduce this issue but I couldn't. Everything works as expected.
Can you provide a reproducible example, please?
Tested with the following code:
From http://localhost:3000/workspaces/create URL, useForm sends this request.
Request URL:
https://api.fake-rest.refine.dev/workspaces
Request Method: POST
- pages/_app.tsx
import { GitHubBanner, Refine } from '@refinedev/core'
import { DevtoolsPanel, DevtoolsProvider } from '@refinedev/devtools'
import { RefineKbar, RefineKbarProvider } from '@refinedev/kbar'
import { RefineSnackbarProvider, ThemedLayoutV2, notificationProvider } from '@refinedev/mui'
import routerProvider, { DocumentTitleHandler, UnsavedChangesNotifier } from '@refinedev/nextjs-router'
import type { NextPage } from 'next'
import { AppProps } from 'next/app'
import { Header } from '@components/header'
import { ColorModeContextProvider } from '@contexts'
import CssBaseline from '@mui/material/CssBaseline'
import GlobalStyles from '@mui/material/GlobalStyles'
import dataProvider from '@refinedev/simple-rest'
import { appWithTranslation, useTranslation } from 'next-i18next'
import { authProvider } from 'src/authProvider'
const API_URL = 'https://api.fake-rest.refine.dev'
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
noLayout?: boolean
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
function MyApp({ Component, pageProps }: AppPropsWithLayout): JSX.Element {
const renderComponent = () => {
if (Component.noLayout) {
return <Component {...pageProps} />
}
return (
<ThemedLayoutV2 Header={() => <Header sticky />}>
<Component {...pageProps} />
</ThemedLayoutV2>
)
}
const { t, i18n } = useTranslation()
const i18nProvider = {
translate: (key: string, params: object) => t(key, params),
changeLocale: (lang: string) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
}
return (
<>
<GitHubBanner />
<RefineKbarProvider>
<ColorModeContextProvider>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: 'auto' } }} />
<RefineSnackbarProvider>
<DevtoolsProvider>
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(API_URL)}
notificationProvider={notificationProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
resources={[
{
name: 'workspaces',
list: '/workspaces',
create: '/workspaces/create',
edit: '/workspaces/edit/:id',
show: '/workspaces/show/:id',
meta: {
canDelete: true,
},
},
]}
options={{
syncWithLocation: true,
warnWhenUnsavedChanges: true,
useNewQueryKeys: true,
}}>
{renderComponent()}
<RefineKbar />
<UnsavedChangesNotifier />
<DocumentTitleHandler />
</Refine>
<DevtoolsPanel />
</DevtoolsProvider>
</RefineSnackbarProvider>
</ColorModeContextProvider>
</RefineKbarProvider>
</>
)
}
export default appWithTranslation(MyApp)
- pages/workspaces/create/index.tsx
import { Create } from '@refinedev/mui'
import { Box, TextField } from '@mui/material'
import { useForm } from '@refinedev/react-hook-form'
import { IResourceComponentsProps, useTranslate } from '@refinedev/core'
const WorkspaceCreate: React.FC<IResourceComponentsProps> = () => {
const translate = useTranslate()
const {
saveButtonProps,
refineCore: { formLoading },
register,
formState: { errors },
} = useForm()
return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box component='form' sx={{ display: 'flex', flexDirection: 'column' }} autoComplete='off'></Box>
<TextField
{...register('title', {
required: 'This field is required',
})}
error={!!(errors as any)?.title}
helperText={(errors as any)?.title?.message}
margin='normal'
fullWidth
InputLabelProps={{ shrink: true }}
type='text'
label={translate('posts.fields.title')}
name='title'
/>
</Create>
)
}
export default WorkspaceCreate
Hey @BatuhanW @alicanerdurmaz
Here is repo I created with the reproducible issue... you can clone it and go to http://localhost:3002/workspaces/create
Hey @Cavdy,
Let's change API_URL to 'http://localhost:3002/api'. After the change, you can see your request in the network tab. We can't see requests for localhost:3000 because there is no server on that port.
Everything works as expected. What do you expect?
Hey @alicanerdurmaz
The resource endpoint specified is workspaces/create but it's using workspaces as you can see from the screenshot you shared. I thought it's suppose to use the appropriate action url which is workspaces/create
@Cavdy Oh, I see. This is expected. converting resource to url is the responsibility of the data provider. You can see how simple-rest generates url here.
I guess you are confused when you give create: "/workspaces/create" to resource and I think you are not wrong, but this definition is for page route, not for api url.
you have 2 options to solve this issue:
- you can create your data provider with swizzle.
- you can give
resource: "/workspaces/createto your data hook.
Thanks @alicanerdurmaz
Is there a way to pass a custom resource (e.g. in a mutate call) and specify where the id should be interpolated? For example, I want to do
mutate({
resource: "/products/:id/customize",
values: {
name: "New Product",
material: "Wood",
},
id: 1,
});
But that will interpret the string literally and pass id to the end of the route. Right now the only work around I've found is to pass an interpolated string and empty id