From 48caff8075733cbe89aa5c9b3424bdb75ee88af9 Mon Sep 17 00:00:00 2001 From: taefi Date: Mon, 24 Feb 2025 00:42:42 +0200 Subject: [PATCH 01/16] feat: add navigation articles for react --- .../building-apps/views/navigate/react.adoc | 175 +++++++++++++++++- 1 file changed, 172 insertions(+), 3 deletions(-) diff --git a/articles/building-apps/views/navigate/react.adoc b/articles/building-apps/views/navigate/react.adoc index 335f726d51..c7d8e0c875 100644 --- a/articles/building-apps/views/navigate/react.adoc +++ b/articles/building-apps/views/navigate/react.adoc @@ -1,12 +1,181 @@ --- title: React page-title: How to navigate to a view in React | Vaadin -description: TODO Write a description -meta-description: TODO Write a meta description +description: Learn how to navigate between React views in a Vaadin application. +meta-description: This guide covers how to navigate to a view in a React application using Vaadin, including creating links and programmatic navigation. order: 10 --- = Navigation in React -TODO \ No newline at end of file +In this guide, you'll learn how to use `` component and `useNavigate` hook of `react-router` to navigate between views. At the end, a mini-tutorial helps you to apply these concepts in a real Vaadin application. + + +== Navigating Between Views Using Links + +The `` component from `react-router` renders a clickable link for navigation. In HTML, it corresponds to an anchor (``) element. + +[TIP] +Links are preferable to programmatic navigation because they *improve accessibility*. They also allow users to open links in new browser tabs. + + +For example, to create a link to a view located at `/my-view`, you can use the following code: + +[source,tsx] +---- +import { NavLink } from 'react-router'; + +My View +---- + +This code creates a clickable link labeled "My View" that navigates to the `/my-view` route when clicked. + + +== Programmatic Navigation + +In some scenarios, you may need to navigate between views programmatically, such as after a form submission or in response to user interactions. For this you can use the `useNavigate` hook of `react-router` to achieve this. + +Here's an example of how to use `useNavigate` for programmatic navigation: + +[source,tsx] +---- +import { useNavigate } from 'react-router'; + +function MyComponent() { +const navigate = useNavigate(); + + const handleClick = () => { + navigate('/target-view'); + }; + + return ( + + ); +} +---- + +In the above example, clicking the button will navigate the user to `/target-view`. + + +== Flow Views + +To navigate from a React view to a Flow view that is implemented in Java, the same principles apply. You can use the `NavLink` component or the `useNavigate` hook to navigate to the Flow view. + + +== Try It + +In this mini-tutorial, you'll learn how to navigate between React views using both *links* and *programmatic navigation*. + + +First, generate a <<{articles}/getting-started/start#,walking skeleton with a React UI>>, <<{articles}/getting-started/import#,open>> it in your IDE, and <<{articles}/getting-started/run#,run>> it. + + +=== Modify the Todo View + +Change the path of the `TodoView` to `todo`. The `TodoView` is stored in the file `@index.tsx` that is located directly under the `views` directory. To change its route to `/todo`, rename the file to `todo.tsx` so that the directory structure looks like this: + +[source] +---- +views +├── @layout.tsx +├── _ErrorHandler.ts +└── todo.tsx +---- + +This is just a convenience step for having a simple and clear Main view for the next steps. + + +=== Create an About View + +You'll start by creating a new view called `AboutView`. This view is going to be the target view of navigation in this mini-tutorial. In the `views` directory, create a new file named [filename]`about.tsx`: + +[source,tsx] +.about.tsx +---- +export default function AboutView() { + return

About View

; +} +---- + +The path for this view is automatically resolved to `/about`, and users can access it by navigating to `\https://example.com/about`. + +=== Create a Main View + +Next, create a new main view. This view is going to be the source of navigation in this mini-tutorial. In the `views` directory, create a new file called `@index.tsx`: + +[source,tsx] +.@index.tsx +---- +export default function MainView() { + return

Main View

; +} +---- + +=== Add a Link to the Main View + +Now, add a link that targets the `AboutView` from the `MainView`. In the `@index.tsx` file, add the following code: + +[source,tsx] +.@index.tsx +---- +import { NavLink } from 'react-router'; + +export default function MainView() { + return ( + <> +

Main View

+ Link to About + + ); +} +---- + +This code creates a clickable link labeled "Link to About" that navigates to the `/about` route when clicked. + +=== Add a Button for Programmatic Navigation + +Now, add a button that navigates to the `AboutView` programmatically. In the `@index.tsx` file, change the codes to have the following code: + +[source,tsx] +.@index.tsx +---- +import { NavLink, useNavigate } from 'react-router'; + +export default function MainView() { + const navigate = useNavigate(); + + const handleClick = () => { + navigate('/about'); + }; + + return ( + <> +

Main View

+ Link to About + + + ); +} +---- + +This code creates a button labeled "Go to About" that navigates to the `/about` route when clicked. + +=== Test the Navigation + +Now, run the application and navigate to the main view. You should see the "Link to About" link and the "Go to About" button. Clicking either of them should navigate you to the `AboutView`. + + +== Final Thoughts + +You've now explored different ways to navigate between views. Here's what you've learned: + +* Creating a navigation link using `NavLink` component from `react-router` library. +* Programmatically navigating using the `useNavigate` hook from `react-router` library. +* Navigating between React views and Flow views. + +Now that you know how to navigate between views, check out the <<../pass-data#,Pass Data to a View>> guide to learn how to pass data to a view while navigating to it. From 2910afaa6e9d19d0c36b3014f1563d9114cbe75b Mon Sep 17 00:00:00 2001 From: taefi Date: Mon, 24 Feb 2025 10:31:34 +0200 Subject: [PATCH 02/16] fix meta desc --- articles/building-apps/views/navigate/react.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/building-apps/views/navigate/react.adoc b/articles/building-apps/views/navigate/react.adoc index c7d8e0c875..eac6c24842 100644 --- a/articles/building-apps/views/navigate/react.adoc +++ b/articles/building-apps/views/navigate/react.adoc @@ -2,7 +2,7 @@ title: React page-title: How to navigate to a view in React | Vaadin description: Learn how to navigate between React views in a Vaadin application. -meta-description: This guide covers how to navigate to a view in a React application using Vaadin, including creating links and programmatic navigation. +meta-description: This guide covers how to navigate to a React or Flow view in Vaadin applications, including creating links and programmatic navigation. order: 10 --- From 1e05666b56a99f786a4f3e6d8715eda229bffb20 Mon Sep 17 00:00:00 2001 From: taefi Date: Mon, 24 Feb 2025 11:38:30 +0200 Subject: [PATCH 03/16] add how to opt out files and directories from routes --- articles/building-apps/views/add-view/react.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/articles/building-apps/views/add-view/react.adoc b/articles/building-apps/views/add-view/react.adoc index beb71b4dba..de7df9e738 100644 --- a/articles/building-apps/views/add-view/react.adoc +++ b/articles/building-apps/views/add-view/react.adoc @@ -65,6 +65,8 @@ Defining the function name as `customersView` or `customerList` does not result The routing uses the React component's name for creating the default automatic title for the view. For example, the title for the `CustomersView` component is `Customers`, and the title for the `HelloWorldView` component is `Hello World`. This automatically-determined title is used when creating the navigation menu based on utilities from the routing API. +Another important convention to consider while naming the views and directories is to use the `_` (underscore) character at the beginning of the file or directory name to instruct the routing system to ignore it. For example, a file named as `_AddressFormComponent.tsx` is ignored when creating routes for views. This is useful for creating utility files and reusable components that are not intended to be available as navigation targets. + The details about the automatic title and the navigation menu are covered in more detail in the <<../navigate#,Navigate to a View>> guide. From 7dfb30d7a294930973f8b2982cdbaecab83ed0ff Mon Sep 17 00:00:00 2001 From: taefi Date: Mon, 24 Feb 2025 14:46:13 +0200 Subject: [PATCH 04/16] add required route params in react --- .../pass-data/route-parameters/react.adoc | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index 332b7652b6..38fa977522 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -9,3 +9,96 @@ order: 10 = Route Parameters in React + +In this guide, you’ll learn how to create a view that accepts a single route parameter. You’ll also explore the differences between required, optional, and wildcard route parameters. Finally, a mini-tutorial will help you apply these concepts in a real Vaadin application. + + +== Required Route Parameters + +To create a React view that accepts a required route parameter, you need to define the parameter name as the view name placed in between curly braces (`{}`) followed by the `.tsx` suffix. For example, to create a view that accepts a required `productId` parameter, you should name the view as `{productId}.tsx`. The directory structure under the `views` will look like this: + +[source] +---- +views/ +└── products/ + └── {productId}.tsx <1> +---- +<1> The view file that accepts the `productId` parameter. The route will be in form of `/products/{productId}`, where `productId` is a required parameter. The routes such as `/products/123`, `/products/prd222`, etc., will match this view. Trying to navigate to `/products` will result in a 404 Not Found error, unless a view matching the route is defined. + +Though, it's technically possible to define the views with parameters directly under the `views` directory, it's recommended to use subdirectories to keep the views organized. + +The routing system also supports having parameters in the middle of route, that means the directories can also have their names defined with a `{param-name}` convention. Here's an example of a view that accepts a required `productId` parameter: + +[source] +---- +views/ +└── products + └── {productId} <1> + └── edit.tsx +---- +<1> The directory that all its children files have `productId` parameter in the middle of their routes. The route will be in form of `/products/{productId}/edit`, where `productId` acts as placeholder for the required parameter, in this case the product ID. The routes such as `/products/123/edit`, `/products/prd222/edit`, etc., will match this view. Trying to navigate to `/products/edit` will result in a 404 Not Found error, unless a view matching the route is defined. + + +=== Accessing Route Parameter Value + +To access the route parameter in the view, use the `useParams` hook from `react-router` in your component. Here’s an example of a view that accepts a required `productId` parameter: + +[source,tsx] +.views/products/{productId}.tsx +---- +import { useParams } from 'react-router'; + +export default function ProductView() { + const { productId } = useParams(); <1> + + const product = useSignal(undefined); + + // Fetch product details for the given ID + useEffect(() => { + ProductEndpoint.getProduct(productId).then(p => product.value = p); + }, [productId]); + + // render the product ... +} +---- +<1> The `useParams` hook returns an object containing the route parameters. In this case, the `productId` parameter is extracted from the object and stored in a variable. + +=== Passing Route Parameter + +To navigate to a view that accepts a route parameter, use the `NavLink` component from `react-router`. Here’s an example of how to create a link to a view located at `/products/123`: + +[source,tsx] +---- +import { NavLink } from 'react-router'; +... +Product 123 +---- + +Another way is to use the `useNavigate` hook to navigate programmatically. Here’s an example of how to use `useNavigate` for programmatic navigation: + +[source,tsx] +---- +import { useNavigate } from 'react-router'; + +function MyComponent() { + const navigate = useNavigate(); + + const handleClick = () => { + navigate('/products/123'); + }; + + return ( + + ); +} +---- + +By using either of the above methods, you can navigate to the `/views/products/{productId}.tsx` view that accepts `productId` route parameter. If you navigate to `/products/123`, the `productId` parameter will be extracted and used in the view to fetch the product details for the given ID. + +If a parameter is expected in the middle of the route, the method for navigating to the view is the same. For example, to navigate to `/products/123/edit`, use the same methods as above. The `useParams` hook will extract the `productId` parameter from the route, and the view will fetch the product details for the given ID. + + +== Optional Route Parameters + From 740643a96c8a5d6060840d1b32bda772e5a790ed Mon Sep 17 00:00:00 2001 From: taefi Date: Thu, 27 Feb 2025 19:36:42 +0200 Subject: [PATCH 05/16] simplify example --- .../views/pass-data/route-parameters/react.adoc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index 38fa977522..9cda8c8168 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -51,14 +51,12 @@ import { useParams } from 'react-router'; export default function ProductView() { const { productId } = useParams(); <1> - const product = useSignal(undefined); - - // Fetch product details for the given ID - useEffect(() => { - ProductEndpoint.getProduct(productId).then(p => product.value = p); - }, [productId]); - - // render the product ... + return ( + <> +

Product Details

+

Product ID: {productId}

+ + ); } ---- <1> The `useParams` hook returns an object containing the route parameters. In this case, the `productId` parameter is extracted from the object and stored in a variable. From 73a54947265c18c963f994625e0f634c161ede79 Mon Sep 17 00:00:00 2001 From: taefi Date: Thu, 27 Feb 2025 21:03:43 +0200 Subject: [PATCH 06/16] fix lint issues --- articles/building-apps/views/navigate/react.adoc | 12 ++++++------ .../views/pass-data/route-parameters/react.adoc | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/articles/building-apps/views/navigate/react.adoc b/articles/building-apps/views/navigate/react.adoc index eac6c24842..7dcf4cb440 100644 --- a/articles/building-apps/views/navigate/react.adoc +++ b/articles/building-apps/views/navigate/react.adoc @@ -20,16 +20,16 @@ The `` component from `react-router` renders a clickable link for navig Links are preferable to programmatic navigation because they *improve accessibility*. They also allow users to open links in new browser tabs. -For example, to create a link to a view located at `/my-view`, you can use the following code: +For example, to create a link to a view located at `/some-view`, you can use the following code: [source,tsx] ---- import { NavLink } from 'react-router'; -My View +Some View ---- -This code creates a clickable link labeled "My View" that navigates to the `/my-view` route when clicked. +This code creates a clickable link labeled "Some View" that navigates to the `/some-view` route when clicked. == Programmatic Navigation @@ -57,7 +57,7 @@ const navigate = useNavigate(); } ---- -In the above example, clicking the button will navigate the user to `/target-view`. +In the above example, clicking the button navigates the user to `/target-view`. == Flow Views @@ -85,10 +85,10 @@ views └── todo.tsx ---- -This is just a convenience step for having a simple and clear Main view for the next steps. +This is a convenience step for having a simple and clear Main view for the next steps. -=== Create an About View +=== Create the About View You'll start by creating a new view called `AboutView`. This view is going to be the target view of navigation in this mini-tutorial. In the `views` directory, create a new file named [filename]`about.tsx`: diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index 9cda8c8168..c6a76edbe6 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -10,12 +10,12 @@ order: 10 = Route Parameters in React -In this guide, you’ll learn how to create a view that accepts a single route parameter. You’ll also explore the differences between required, optional, and wildcard route parameters. Finally, a mini-tutorial will help you apply these concepts in a real Vaadin application. +In this guide, you’ll learn how to create a view that accepts a single route parameter. You’ll also explore the differences between required, optional, and wildcard route parameters. Finally, a mini-tutorial helps you to apply these concepts in a real Vaadin application. == Required Route Parameters -To create a React view that accepts a required route parameter, you need to define the parameter name as the view name placed in between curly braces (`{}`) followed by the `.tsx` suffix. For example, to create a view that accepts a required `productId` parameter, you should name the view as `{productId}.tsx`. The directory structure under the `views` will look like this: +To create a React view that accepts a required route parameter, you need to define the parameter name as the view name placed in between curly braces (`{}`) followed by the `.tsx` suffix. For example, to create a view that accepts a required `productId` parameter, you should name the view as `{productId}.tsx`. The directory structure under the `views` looks like this: [source] ---- @@ -23,7 +23,7 @@ views/ └── products/ └── {productId}.tsx <1> ---- -<1> The view file that accepts the `productId` parameter. The route will be in form of `/products/{productId}`, where `productId` is a required parameter. The routes such as `/products/123`, `/products/prd222`, etc., will match this view. Trying to navigate to `/products` will result in a 404 Not Found error, unless a view matching the route is defined. +<1> The view file that accepts the `productId` parameter. The route is in form of `/products/{productId}`, where `productId` is a required parameter. The routes such as `/products/123`, `/products/prd222`, etc., match this view. Trying to navigate to `/products` results in a 404 Not Found error, unless a view matching the route is defined. Though, it's technically possible to define the views with parameters directly under the `views` directory, it's recommended to use subdirectories to keep the views organized. @@ -36,7 +36,7 @@ views/ └── {productId} <1> └── edit.tsx ---- -<1> The directory that all its children files have `productId` parameter in the middle of their routes. The route will be in form of `/products/{productId}/edit`, where `productId` acts as placeholder for the required parameter, in this case the product ID. The routes such as `/products/123/edit`, `/products/prd222/edit`, etc., will match this view. Trying to navigate to `/products/edit` will result in a 404 Not Found error, unless a view matching the route is defined. +<1> The directory that all its children files have `productId` parameter in the middle of their routes. The route is in form of `/products/{productId}/edit`, where `productId` acts as placeholder for the required parameter, in this case the product ID. The routes such as `/products/123/edit`, `/products/prd222/edit`, etc., match this view. Trying to navigate to `/products/edit` results in a 404 Not Found error, unless a view matching the route is defined. === Accessing Route Parameter Value @@ -93,9 +93,9 @@ function MyComponent() { } ---- -By using either of the above methods, you can navigate to the `/views/products/{productId}.tsx` view that accepts `productId` route parameter. If you navigate to `/products/123`, the `productId` parameter will be extracted and used in the view to fetch the product details for the given ID. +By using either of the above methods, you can navigate to the `/views/products/{productId}.tsx` view that accepts `productId` route parameter. If you navigate to `/products/123`, the `productId` parameter is extracted and used in the view to fetch the product details for the given ID. -If a parameter is expected in the middle of the route, the method for navigating to the view is the same. For example, to navigate to `/products/123/edit`, use the same methods as above. The `useParams` hook will extract the `productId` parameter from the route, and the view will fetch the product details for the given ID. +If a parameter is expected in the middle of the route, the method for navigating to the view is the same. For example, to navigate to `/products/123/edit`, use the same methods as above. The `useParams` hook extracts the `productId` parameter from the route, and the view fetches the product details for the given ID. == Optional Route Parameters From a4066a51a52adf1b9ab8afb88b6a038b54b605f2 Mon Sep 17 00:00:00 2001 From: taefi Date: Fri, 28 Feb 2025 00:02:39 +0200 Subject: [PATCH 07/16] add optional parameters section --- .../pass-data/route-parameters/react.adoc | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index c6a76edbe6..bbd9555891 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -100,3 +100,55 @@ If a parameter is expected in the middle of the route, the method for navigating == Optional Route Parameters +Views can also have optional route parameters. This means that the view can be navigated to with or without the parameter. To make a route parameter optional, use the doubly curly braces around the parameter name when naming the view file. For example, to create a view that accepts an optional `categoryName` parameter, name the view as `{{categoryName}}.tsx`. The directory structure under the `views` looks like this: + +[source] +---- +views/ +└── products + └── {{categoryName}}.tsx <1> +---- + +<1> The view file that accepts the `categoryName` optional parameter. The route for this view is in form of `/products/{{categoryName}}`, where `categoryName` is an optional parameter. The routes such as `/products`, `/products/electronics`, `/products/clothing`, etc., match this view. Note that a view optional parameter should be able to handle both cases when the parameter is present and when it's not. Here's an example of a view that accepts an optional `categoryName` parameter: + +[source,tsx] +.views/products/{{categoryName}}.tsx +---- +import { useParams } from "react-router"; +import { ProductService } from "Frontend/generated/endpoints.js"; +import { useCallback, useEffect } from "react"; +import { useSignal } from "@vaadin/hilla-react-signals"; + +export default function ProductByCategoriesView() { + const { categoryName } = useParams(); <1> + const products = useSignal([]); + + const fetchData = useCallback(async()=> { + if (categoryName == undefined) { <2> + products.value = await ProductService.allProducts(); + } else { + products.value = await ProductService.productsInCategory(categoryName); + } + }, [categoryName]); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + return ( + <> +

Products from {categoryName ? `'${categoryName}' category` : "all categories"}:

+
+
    {products.value.map((product) => ( +
  • {product}
  • + ))}
+
+ + ); +} +---- +<1> The `useParams` hook returns an object containing the route parameters. In this case, the `categoryName` parameter is extracted from the object and stored in a variable. +<2> The `categoryName` parameter is checked to determine whether it's present or not. If it's not present, all products are fetched. Otherwise, products in the specified category are fetched. + +In the above example, the `ProductByCategoriesView` fetches all products when the `categoryName` parameter is not present. When the `categoryName` parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the `categoryName` parameter is not present. + From f2fc4f64991ff8f3409399c9f0c553f6491df4f3 Mon Sep 17 00:00:00 2001 From: taefi Date: Fri, 28 Feb 2025 11:09:15 +0200 Subject: [PATCH 08/16] fix punctuation and some grammatical issues --- .../pass-data/route-parameters/react.adoc | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index bbd9555891..0e4211f6bb 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -10,12 +10,12 @@ order: 10 = Route Parameters in React -In this guide, you’ll learn how to create a view that accepts a single route parameter. You’ll also explore the differences between required, optional, and wildcard route parameters. Finally, a mini-tutorial helps you to apply these concepts in a real Vaadin application. +In this guide, you’ll learn how to create a view that accepts a single route parameter. You’ll also explore the differences between required, optional, and wildcard route parameters. Finally, a mini-tutorial helps you apply these concepts in a real Vaadin application. == Required Route Parameters -To create a React view that accepts a required route parameter, you need to define the parameter name as the view name placed in between curly braces (`{}`) followed by the `.tsx` suffix. For example, to create a view that accepts a required `productId` parameter, you should name the view as `{productId}.tsx`. The directory structure under the `views` looks like this: +To create a React view that accepts a required route parameter, you need to define the parameter name as the view name placed between curly braces (`{}`) followed by the `.tsx` suffix. For example, to create a view that accepts a required `productId` parameter, you should name the view `{productId}.tsx`. The directory structure under the `views` looks like this: [source] ---- @@ -23,11 +23,11 @@ views/ └── products/ └── {productId}.tsx <1> ---- -<1> The view file that accepts the `productId` parameter. The route is in form of `/products/{productId}`, where `productId` is a required parameter. The routes such as `/products/123`, `/products/prd222`, etc., match this view. Trying to navigate to `/products` results in a 404 Not Found error, unless a view matching the route is defined. +<1> The view file that accepts the `productId` parameter. The route is in the form of `/products/{productId}`, where `productId` is a required parameter. Routes such as `/products/123`, `/products/prd222`, etc., match this view. Trying to navigate to `/products` results in a 404 Not Found error unless a view matching the route is defined. -Though, it's technically possible to define the views with parameters directly under the `views` directory, it's recommended to use subdirectories to keep the views organized. +Though it is technically possible to define the views with parameters directly under the `views` directory, it is recommended to use subdirectories to keep the views organized. -The routing system also supports having parameters in the middle of route, that means the directories can also have their names defined with a `{param-name}` convention. Here's an example of a view that accepts a required `productId` parameter: +The routing system also supports having parameters in the middle of the route, which means the directories can also have their names defined with a `{param-name}` convention. Here is an example of a view that accepts a required `productId` parameter: [source] ---- @@ -36,12 +36,11 @@ views/ └── {productId} <1> └── edit.tsx ---- -<1> The directory that all its children files have `productId` parameter in the middle of their routes. The route is in form of `/products/{productId}/edit`, where `productId` acts as placeholder for the required parameter, in this case the product ID. The routes such as `/products/123/edit`, `/products/prd222/edit`, etc., match this view. Trying to navigate to `/products/edit` results in a 404 Not Found error, unless a view matching the route is defined. - +<1> The directory where all its children files have the `productId` parameter in the middle of their routes. The route is in the form of `/products/{productId}/edit`, where `productId` acts as a placeholder for the required parameter, in this case, the product ID. Routes such as `/products/123/edit`, `/products/prd222/edit`, etc., match this view. Trying to navigate to `/products/edit` results in a 404 Not Found error unless a view matching the route is defined. === Accessing Route Parameter Value -To access the route parameter in the view, use the `useParams` hook from `react-router` in your component. Here’s an example of a view that accepts a required `productId` parameter: +To access the route parameter in the view, use the `useParams` hook from `react-router` in your component. Here is an example of a view that accepts a required `productId` parameter: [source,tsx] .views/products/{productId}.tsx @@ -63,7 +62,7 @@ export default function ProductView() { === Passing Route Parameter -To navigate to a view that accepts a route parameter, use the `NavLink` component from `react-router`. Here’s an example of how to create a link to a view located at `/products/123`: +To navigate to a view that accepts a route parameter, use the `NavLink` component from `react-router`. Here is an example of how to create a link to a view located at `/products/123`: [source,tsx] ---- @@ -72,7 +71,7 @@ import { NavLink } from 'react-router'; Product 123 ---- -Another way is to use the `useNavigate` hook to navigate programmatically. Here’s an example of how to use `useNavigate` for programmatic navigation: +Another way is to use the `useNavigate` hook to navigate programmatically. Here is an example of how to use `useNavigate` for programmatic navigation: [source,tsx] ---- @@ -93,14 +92,13 @@ function MyComponent() { } ---- -By using either of the above methods, you can navigate to the `/views/products/{productId}.tsx` view that accepts `productId` route parameter. If you navigate to `/products/123`, the `productId` parameter is extracted and used in the view to fetch the product details for the given ID. +By using either of the above methods, you can navigate to the `/views/products/{productId}.tsx` view that accepts the `productId` route parameter. If you navigate to `/products/123`, the `productId` parameter is extracted and used in the view to fetch the product details for the given ID. If a parameter is expected in the middle of the route, the method for navigating to the view is the same. For example, to navigate to `/products/123/edit`, use the same methods as above. The `useParams` hook extracts the `productId` parameter from the route, and the view fetches the product details for the given ID. - == Optional Route Parameters -Views can also have optional route parameters. This means that the view can be navigated to with or without the parameter. To make a route parameter optional, use the doubly curly braces around the parameter name when naming the view file. For example, to create a view that accepts an optional `categoryName` parameter, name the view as `{{categoryName}}.tsx`. The directory structure under the `views` looks like this: +Views can also have optional route parameters. This means that the view can be navigated to with or without the parameter. To make a route parameter optional, use double curly braces around the parameter name when naming the view file. For example, to create a view that accepts an optional `categoryName` parameter, name the view `{{categoryName}}.tsx`. The directory structure under the `views` looks like this: [source] ---- @@ -109,7 +107,7 @@ views/ └── {{categoryName}}.tsx <1> ---- -<1> The view file that accepts the `categoryName` optional parameter. The route for this view is in form of `/products/{{categoryName}}`, where `categoryName` is an optional parameter. The routes such as `/products`, `/products/electronics`, `/products/clothing`, etc., match this view. Note that a view optional parameter should be able to handle both cases when the parameter is present and when it's not. Here's an example of a view that accepts an optional `categoryName` parameter: +<1> The view file that accepts the `categoryName` optional parameter. The route for this view is in the form of `/products/{{categoryName}}`, where `categoryName` is an optional parameter. Routes such as `/products`, `/products/electronics`, `/products/clothing`, etc., match this view. Note that a view with an optional parameter should be able to handle both cases when the parameter is present and when it is not. Here is an example of a view that accepts an optional `categoryName` parameter: [source,tsx] .views/products/{{categoryName}}.tsx @@ -148,7 +146,6 @@ export default function ProductByCategoriesView() { } ---- <1> The `useParams` hook returns an object containing the route parameters. In this case, the `categoryName` parameter is extracted from the object and stored in a variable. -<2> The `categoryName` parameter is checked to determine whether it's present or not. If it's not present, all products are fetched. Otherwise, products in the specified category are fetched. - -In the above example, the `ProductByCategoriesView` fetches all products when the `categoryName` parameter is not present. When the `categoryName` parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the `categoryName` parameter is not present. +<2> The `categoryName` parameter is checked to determine whether it is present or not. If it is not present, all products are fetched. Otherwise, products in the specified category are fetched. +In the above example, the `ProductByCategoriesView` fetches all products when the `categoryName` parameter is not present. When the `categoryName` parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the `categoryName` parameter is not present. \ No newline at end of file From a6038ff7139ed7570f9c7d62842a7543a0c1ad69 Mon Sep 17 00:00:00 2001 From: taefi Date: Fri, 28 Feb 2025 11:11:13 +0200 Subject: [PATCH 09/16] format --- .../views/pass-data/route-parameters/react.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index 0e4211f6bb..d4707f021f 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -148,4 +148,7 @@ export default function ProductByCategoriesView() { <1> The `useParams` hook returns an object containing the route parameters. In this case, the `categoryName` parameter is extracted from the object and stored in a variable. <2> The `categoryName` parameter is checked to determine whether it is present or not. If it is not present, all products are fetched. Otherwise, products in the specified category are fetched. -In the above example, the `ProductByCategoriesView` fetches all products when the `categoryName` parameter is not present. When the `categoryName` parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the `categoryName` parameter is not present. \ No newline at end of file +In the above example, the `ProductByCategoriesView` fetches all products when the `categoryName` parameter is not present. When the `categoryName` parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the `categoryName` parameter is not present. + + +== Try It From 51d2b64c06e7af8f964d94cd1e944d616aca25e0 Mon Sep 17 00:00:00 2001 From: taefi Date: Fri, 28 Feb 2025 14:20:12 +0200 Subject: [PATCH 10/16] simplify example --- .../pass-data/route-parameters/react.adoc | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index d4707f021f..b0a0d6fcf1 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -114,24 +114,20 @@ views/ ---- import { useParams } from "react-router"; import { ProductService } from "Frontend/generated/endpoints.js"; -import { useCallback, useEffect } from "react"; +import { useEffect } from "react"; import { useSignal } from "@vaadin/hilla-react-signals"; export default function ProductByCategoriesView() { - const { categoryName } = useParams(); <1> + const { categoryName } = useParams(); const products = useSignal([]); - const fetchData = useCallback(async()=> { - if (categoryName == undefined) { <2> - products.value = await ProductService.allProducts(); + useEffect(() => { + if (categoryName == undefined) { + ProductService.allProducts().then((data) => products.value = data); } else { - products.value = await ProductService.productsInCategory(categoryName); + ProductService.productsInCategory(categoryName).then((data) => products.value = data); } - }, [categoryName]); - - useEffect(() => { - fetchData(); - }, [fetchData]); + }, []); return ( <> From 722f9d0d935d9d715ca84ae7d4e658a1850203d6 Mon Sep 17 00:00:00 2001 From: taefi Date: Fri, 28 Feb 2025 14:48:16 +0200 Subject: [PATCH 11/16] add highlighting to examples --- .../views/pass-data/route-parameters/react.adoc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index b0a0d6fcf1..78d5853eba 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -45,10 +45,14 @@ To access the route parameter in the view, use the `useParams` hook from `react- [source,tsx] .views/products/{productId}.tsx ---- +// tag::snippet[] import { useParams } from 'react-router'; +// end::snippet[] export default function ProductView() { +// tag::snippet[] const { productId } = useParams(); <1> +// end::snippet[] return ( <> @@ -75,7 +79,9 @@ Another way is to use the `useNavigate` hook to navigate programmatically. Here [source,tsx] ---- +// tag::snippet[] import { useNavigate } from 'react-router'; +// end::snippet[] function MyComponent() { const navigate = useNavigate(); @@ -118,11 +124,15 @@ import { useEffect } from "react"; import { useSignal } from "@vaadin/hilla-react-signals"; export default function ProductByCategoriesView() { - const { categoryName } = useParams(); +// tag::snippet[] + const { categoryName } = useParams(); <1> +// end::snippet[] const products = useSignal([]); useEffect(() => { - if (categoryName == undefined) { +// tag::snippet[] + if (categoryName == undefined) { <2> +// end::snippet[] ProductService.allProducts().then((data) => products.value = data); } else { ProductService.productsInCategory(categoryName).then((data) => products.value = data); From d6bd3c42601a3edd5a9684ccf70531ea7f8d74e9 Mon Sep 17 00:00:00 2001 From: taefi Date: Fri, 28 Feb 2025 14:56:02 +0200 Subject: [PATCH 12/16] add highlighting to examples --- .../building-apps/views/pass-data/route-parameters/react.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index 78d5853eba..12c3c60adc 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -157,4 +157,8 @@ export default function ProductByCategoriesView() { In the above example, the `ProductByCategoriesView` fetches all products when the `categoryName` parameter is not present. When the `categoryName` parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the `categoryName` parameter is not present. +== Wildcard Route Parameters + +Wildcard route parameters are used to match any number of URL segments. To create a view that accepts a wildcard route parameter, use the character followed by the parameter name when naming the view file. For example, to create a view that accepts a wildcard `path` parameter, name the view `*.tsx`. The directory structure under the `views` looks like this: + == Try It From 6f2ef7415d580d745d2efb58670aed45ede0424f Mon Sep 17 00:00:00 2001 From: taefi Date: Mon, 3 Mar 2025 11:15:30 +0200 Subject: [PATCH 13/16] add details and example for wildcard route parameters. --- .../pass-data/route-parameters/react.adoc | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/react.adoc index 12c3c60adc..f05f878e28 100644 --- a/articles/building-apps/views/pass-data/route-parameters/react.adoc +++ b/articles/building-apps/views/pass-data/route-parameters/react.adoc @@ -159,6 +159,40 @@ In the above example, the `ProductByCategoriesView` fetches all products when th == Wildcard Route Parameters -Wildcard route parameters are used to match any number of URL segments. To create a view that accepts a wildcard route parameter, use the character followed by the parameter name when naming the view file. For example, to create a view that accepts a wildcard `path` parameter, name the view `*.tsx`. The directory structure under the `views` looks like this: +Wildcard route parameters are used to match any number of URL segments. This means when a URL cannot be matched with the other defined routes, the wildcard route is picked as the fallback to handle the navigation. One of the common cases of defining wildcard route parameters when defining the routes in a React application is to properly handle the navigation of users when the route is not found. As the default way of defining the routes is through defining the view files and proper directory structure, adding wildcard route parameters should be done by defining the file name as `{...wildcard}.tsx`. The literal value `wildcard` can be anything that is supported by the filesystem as the filename, but Vaadin recommends to use the `{...wildcard}.tsx` as a conventional standard to make it more readable and intuitive. An example of a view that accepts a wildcard route parameter is shown below: + +[source] +---- +views/ +├── @index.tsx +├── about.tsx +├── contact-us.tsx +└── {...wildcard}.tsx <1> +---- +<1> The view file that accepts the wildcard route parameter. + +The routes that are matched with this view depends on the other defined routes. In this case the `/`, `/about`, and `/contact-us` are mapped to their respective views, and if the user tries to navigate to any other routes such as `/123`, `/orders`, or even `/about/789`, then the `{...wildcard}.tsx` is matched as the fallback, accepting the whole unmatched segment of the URL as the wildcard parameter. The view can then handle the navigation and display a custom 404 Not Found page or redirect the user to the home page. Here is an example of a view that accepts a wildcard route parameter: + +[source, tsx] +.views/{...wildcard}.tsx +---- +import { NavLink, useParams } from "react-router"; + +export default function WildcardView() { + const wildcard = useParams()['*']; <1> + return ( + <> +

Page Not Found!

+
+ The '/{wildcard}' route does not exist. + Go back to the home page. +
+ + ); +} +---- +<1> The wildcard route parameter can be extracted using the `useParams` hook, which is stored in the params object with the `*` (asterisk) as the key. The matched wildcard parameters can have many segments, and all the segments is extracted at once when reading the params object with the `*` (asterisk) as the key. + + == Try It From 6eaaebe55927fcf2489d4f758a1a3aeb117ad267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Holmstr=C3=B6m?= Date: Mon, 3 Mar 2025 12:35:40 +0200 Subject: [PATCH 14/16] Rename react.adoc to hilla.adoc --- articles/building-apps/views/add-view/{react.adoc => hilla.adoc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename articles/building-apps/views/add-view/{react.adoc => hilla.adoc} (100%) diff --git a/articles/building-apps/views/add-view/react.adoc b/articles/building-apps/views/add-view/hilla.adoc similarity index 100% rename from articles/building-apps/views/add-view/react.adoc rename to articles/building-apps/views/add-view/hilla.adoc From 48bd5ecfdab875972601121997c315cab810eee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Holmstr=C3=B6m?= Date: Mon, 3 Mar 2025 12:35:59 +0200 Subject: [PATCH 15/16] Rename react.adoc to hilla.adoc --- articles/building-apps/views/navigate/{react.adoc => hilla.adoc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename articles/building-apps/views/navigate/{react.adoc => hilla.adoc} (100%) diff --git a/articles/building-apps/views/navigate/react.adoc b/articles/building-apps/views/navigate/hilla.adoc similarity index 100% rename from articles/building-apps/views/navigate/react.adoc rename to articles/building-apps/views/navigate/hilla.adoc From 07897b99117ee63aff34db41367909bd1bcb3887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petter=20Holmstr=C3=B6m?= Date: Mon, 3 Mar 2025 12:36:18 +0200 Subject: [PATCH 16/16] Rename react.adoc to hilla.adoc --- .../views/pass-data/route-parameters/{react.adoc => hilla.adoc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename articles/building-apps/views/pass-data/route-parameters/{react.adoc => hilla.adoc} (100%) diff --git a/articles/building-apps/views/pass-data/route-parameters/react.adoc b/articles/building-apps/views/pass-data/route-parameters/hilla.adoc similarity index 100% rename from articles/building-apps/views/pass-data/route-parameters/react.adoc rename to articles/building-apps/views/pass-data/route-parameters/hilla.adoc