fix(system): missing useHref logic (#2943)

* chore(deps): update pnpm-lock.yaml

* fix(system): missing useHref logic

* chore(docs): restructure heading

* feat(docs): add useHref content to routing page

* fix(hooks): revise useAriaLink onClick logic

* chore(deps): bump @react-types/shared

* chore(deps): bump @react-types/shared

* fix(hooks): add missing parameters

* fix(pagination): add missing parameters

* feat(changeset): add missing router.open parameters due to rounter change

* fix(docs): onSelectionChange type

* refactor(changeset): revise changeset message

* chore(deps): lock deps

* chore(hooks): bump @react-types/shared to 3.23.0

* chore(changeset): remove this changeset since it is already in canary
This commit is contained in:
աӄա 2024-09-10 23:21:31 +08:00 committed by GitHub
parent 3ed05abe10
commit 4ac7674d1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 94 additions and 20 deletions

View File

@ -0,0 +1,5 @@
---
"@nextui-org/system": patch
---
Fix missing `useHref` logic (#2934)

View File

@ -17,7 +17,7 @@ component configures all NextUI components within it to navigate using the clien
Set this up once in the root of your app, and any NextUI component with the href prop will automatically navigate
using your router.
### NextUIProvider Setup
## NextUIProvider Setup
The `NextUIProvider` accepts a prop called `navigate`. This should be set to a function received from your
router for performing a client side navigation programmatically. The following example shows the general
@ -43,9 +43,9 @@ function App() {
<Spacer y={2} />
### Next.js
## Next.js
#### App Router
### App Router
Go to your `app/providers.tsx` or `app/providers.jsx` (create it if it doesn't exist) and add the
`useRouter` hook from `next/navigation`, it returns a router object that can be used to perform navigation.
@ -95,15 +95,49 @@ export default function RootLayout({children}: { children: React.ReactNode }) {
> **Note**: Skip this step if you already set up the `NextUIProvider` in your app.
#### Add useHref (Optional)
If you are using the Next.js [basePath](https://nextjs.org/docs/app/api-reference/next-config-js/basePath) setting, you'll need to configure an environment variable to access it.
```js
// next.config.js
const basePath = '...';
const nextConfig = {
basePath,
env: {
BASE_PATH: basePath
}
};
```
Then, provide a custom `useHref` function to prepend it to the href for all links.
```tsx {9,12}
// app/providers.tsx
'use client'
import {NextUIProvider} from '@nextui-org/react';
import {useRouter} from 'next/navigation'
export function Providers({children}: { children: React.ReactNode }) {
const router = useRouter();
const useHref = (href: string) => process.env.BASE_PATH + href;
return (
<NextUIProvider navigate={router.push} useHref={useHref}>
{children}
</NextUIProvider>
)
}
```
</Steps>
#### Pages Router
### Pages Router
Go to pages`/_app.js` or `pages/_app.tsx` (create it if it doesn't exist) and add the`useRouter` hook
from `next/router`, it returns a router object that can be used to perform navigation.
```tsx {7}
```tsx {7,10}
// pages/_app.tsx
import type { AppProps } from 'next/app';
import {NextUIProvider} from '@nextui-org/react';
@ -122,23 +156,47 @@ function MyApp({ Component, pageProps }: AppProps) {
export default MyApp;
```
### React Router
When using the [basePath](https://nextjs.org/docs/app/api-reference/next-config-js/basePath) configuration option, provide a `useHref` option to the router passed to Provider to prepend it to links automatically.
```tsx {8,11}
// pages/_app.tsx
import type { AppProps } from 'next/app';
import {NextUIProvider} from '@nextui-org/react';
import {useRouter} from 'next/router';
function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter();
const useHref = (href: string) => router.basePath + href;
return (
<NextUIProvider navigate={router.push} useHref={useHref}>
<Component {...pageProps} />
</NextUIProvider>
)
}
export default MyApp;
```
## React Router
The `useNavigate` hook from `react-router-dom` returns a `navigate` function that can be used to perform navigation.
The `useHref` hook can also be provided if you're using React Router's `basename` option. Ensure that the component that calls useNavigate and renders Provider is inside the router component (e.g. `BrowserRouter`) so that it has access to React Router's internal context. The React Router `<Routes>` element should also be defined inside `<NextUIProvider>` so that links inside the rendered routes have access to the router.
Go to the `App` file commonly called `App.jsx` or `App.tsx`, add the `useNavigate` hook and pass the
`navigate` function to the `NextUIProvider`:
```jsx {6,9}
// App.tsx or App.jsx
import {BrowserRouter, useNavigate} from 'react-router-dom';
import {BrowserRouter, useNavigate, useHref} from 'react-router-dom';
import {NextUIProvider} from '@nextui-org/react';
function App() {
const navigate = useNavigate();
return (
<NextUIProvider navigate={navigate}>
<NextUIProvider navigate={navigate} useHref={useHref}>
{/* Your app here... */}
<Routes>
<Route path="/" element={<HomePage />} />
@ -164,17 +222,16 @@ component (e.g. `BrowserRouter`) so that it has access to React Router's interna
element should also be defined inside `NextUIProvider` so that links inside the rendered routes have access
to the router.
## Remix
### Remix
Remix uses React Router under the hood, so the same `useNavigate` hook described above also works in Remix
Remix uses React Router under the hood, so the same `useNavigate` and `useHref` hook described above also works in Remix
apps. `NextUIProvider` should be rendered at the `root` of each page that includes NextUI components, or in
`app/root.tsx` to add it to all pages. See the [Remix docs](https://remix.run/docs/en/main/file-conventions/root)
for more details.
```jsx {14}
// app/root.tsx
import {useNavigate, Outlet} from '@remix-run/react';
import {useNavigate, useHref, Outlet} from '@remix-run/react';
import {NextUIProvider} from '@nextui-org/react';
export default function App() {
@ -186,7 +243,7 @@ export default function App() {
{/* ... */}
</head>
<body>
<NextUIProvider navigate={navigate}>
<NextUIProvider navigate={navigate} useHref={useHref}>
<Outlet />
</NextUIProvider>
{/* ... */}
@ -196,8 +253,7 @@ export default function App() {
}
```
### TanStack
## TanStack
To use [TanStack Router](https://tanstack.com/router/latest) with NextUI, render NextUI's RouterProvider inside your root route. Use `router.navigate` in the `navigate` prop, and `router.buildLocation` in the `useHref` prop.
@ -219,8 +275,7 @@ function RootRoute() {
}
```
### Usage examples
## Usage examples
Now that you have set up the `NextUIProvider` in your app, you can use the `href` prop in the `Tabs`,
`Listbox` and `Dropdown` items to navigate between pages.

View File

@ -1,5 +1,6 @@
import type {ModalProviderProps} from "@react-aria/overlays";
import type {ProviderContextProps} from "./provider-context";
import type {Href} from "@react-types/shared";
import {I18nProvider, I18nProviderProps} from "@react-aria/i18n";
import {RouterProvider} from "@react-aria/utils";
@ -31,11 +32,20 @@ export interface NextUIProviderProps
* Link, Menu, Tabs, Table, etc.
*/
navigate?: (path: string) => void;
/**
* Convert an `href` provided to a link component to a native `href`
* For example, a router might accept hrefs relative to a base path,
* or offer additional custom ways of specifying link destinations.
* The original href specified on the link is passed to the navigate function of the RouterProvider,
* and useHref is used to generate the full native href to put on the actual DOM element.
*/
useHref?: (href: Href) => string;
}
export const NextUIProvider: React.FC<NextUIProviderProps> = ({
children,
navigate,
useHref,
disableAnimation = false,
disableRipple = false,
skipFramerMotionAnimations = disableAnimation,
@ -50,7 +60,11 @@ export const NextUIProvider: React.FC<NextUIProviderProps> = ({
let contents = children;
if (navigate) {
contents = <RouterProvider navigate={navigate}>{contents}</RouterProvider>;
contents = (
<RouterProvider navigate={navigate} useHref={useHref}>
{contents}
</RouterProvider>
);
}
const context = useMemo<ProviderContextProps>(() => {