Inferring Parameters
Now we've learned about the useNavigation
hook and how to handle navigation with Refine. In this step, we'll be updating components to benefit from the parameter inference of Refine.
When integrated with a router provider, Refine infers the parameters from route definitions and incorporates them into its hooks and components, eliminating the need for manual passing of resource
, id
and action
parameters.
You can always pass the parameters manually if you want to override the inferred parameters.
Updating the ListProducts
Component
Let's update our <ListProducts />
component and omit the resource
parameter from the useTable
hook.
Update your src/pages/products/list.tsx
file by adding the following lines:
import { useTable, useMany } from "@refinedev/core";
export const ListProducts = () => {
const {
tableQuery: { data, isLoading },
current,
setCurrent,
pageCount,
sorters,
setSorters,
} = useTable({
resource: "products",
pagination: { current: 1, pageSize: 10 },
sorters: { initial: [{ field: "id", order: "asc" }] },
});
/* ... */
};
Updating the ShowProduct
Component
Let's update our <ShowProduct />
component and omit the resource
and id
parameters. Remember that previously we've hard-coded the id
parameter. Now we'll be letting Refine to infer the id
parameter from the route definition and dynamically fetch the product.
We'll also start using useShow
hook which is wrapper around useOne
. Unlike the useOne hook, it offers inference capabilities, eliminating the need to explicitly pass resource
and id
parameters
Update your src/pages/products/show.tsx
file by adding the following lines:
import { useShow } from "@refinedev/core";
export const ShowProduct = () => {
const { data, isLoading } = useOne({ resource: "products", id: 123 });
const { query } = useShow();
/* ... */
};
Updating the EditProduct
Component
Let's update our <EditProduct />
component and omit the resource
, action
and id
parameters from the useForm
hook. Just like the <ShowProduct />
component, we'll be letting Refine to infer the id
parameter from the route definition. Since we've defined the edit
action in our resource definition, Refine will also infer the action
parameter as edit
.
Update your src/pages/products/edit.tsx
file by adding the following lines:
import { useForm, useSelect } from "@refinedev/core";
export const EditProduct = () => {
const { onFinish, mutation, query } = useForm({
action: "edit",
resource: "products",
id: "123",
});
const { onFinish, mutation, query } = useForm();
/* ... */
};
Updating the CreateProduct
Component
Let's update our <CreateProduct />
component and omit the resource
and action
parameters from the useForm
hook. Since we've defined the create
action in our resource definition, Refine will also infer the action
parameter as create
.
Update your src/pages/products/create.tsx
file by adding the following lines:
import { useForm, useSelect } from "@refinedev/core";
export const CreateProduct = () => {
const { onFinish, mutation } = useForm({
action: "create",
resource: "products",
});
const { onFinish, mutation } = useForm();
/* ... */
};
Now you should be able to see that our components are working as expected. We've successfully updated our components to benefit from the parameter inference of Refine.
In the next step, we'll be learning about how to handle redirects in our app.
import { useTable, useMany, useNavigation } from "@refinedev/core"; import { Link } from "react-router"; export const ListProducts = () => { const { tableQuery: { data, isLoading }, current, setCurrent, pageCount, sorters, setSorters, } = useTable({ resource: "protected-products", pagination: { current: 1, pageSize: 10 }, sorters: { initial: [{ field: "id", order: "asc" }] }, }); // You can also use methods like show or list to trigger navigation. // We're using url methods to provide more semantically correct html. const { showUrl, editUrl } = useNavigation(); const { data: categories } = useMany({ resource: "categories", ids: data?.data?.map((product) => product.category?.id) ?? [], }); if (isLoading) { return <div>Loading...</div>; } const onPrevious = () => { if (current > 1) { setCurrent(current - 1); } }; const onNext = () => { if (current < pageCount) { setCurrent(current + 1); } }; const onPage = (page: number) => { setCurrent(page); }; const getSorter = (field: string) => { const sorter = sorters?.find((sorter) => sorter.field === field); if (sorter) { return sorter.order; } }; const onSort = (field: string) => { const sorter = getSorter(field); setSorters( sorter === "desc" ? [] : [ { field, order: sorter === "asc" ? "desc" : "asc", }, ], ); }; const indicator = { asc: "⬆️", desc: "⬇️" }; return ( <div> <h1>Products</h1> <table> <thead> <tr> <th onClick={() => onSort("id")}> ID {indicator[getSorter("id")]} </th> <th onClick={() => onSort("name")}> Name {indicator[getSorter("name")]} </th> <th>Category</th> <th onClick={() => onSort("material")}> Material {indicator[getSorter("material")]} </th> <th onClick={() => onSort("price")}> Price {indicator[getSorter("price")]} </th> <th>Actions</th> </tr> </thead> <tbody> {data?.data?.map((product) => ( <tr key={product.id}> <td>{product.id}</td> <td>{product.name}</td> <td> { categories?.data?.find( (category) => category.id == product.category?.id, )?.title } </td> <td>{product.material}</td> <td>{product.price}</td> <td> <Link to={showUrl("protected-products", product.id)}>Show</Link> <Link to={editUrl("protected-products", product.id)}>Edit</Link> </td> </tr> ))} </tbody> </table> <div className="pagination"> <button type="button" onClick={onPrevious}> {"<"} </button> <div> {current - 1 > 0 && ( <span onClick={() => onPage(current - 1)}>{current - 1}</span> )} <span className="current">{current}</span> {current + 1 < pageCount && ( <span onClick={() => onPage(current + 1)}>{current + 1}</span> )} </div> <button type="button" onClick={onNext}> {">"} </button> </div> </div> ); };