Step 2: Connecting Search State with the URL
Changing the Default Search Functionality
When the user enters a query using the SearchBar
, you can store the query in the URL so that it can be persisted when sharing a link to your application. The first step is to change what happens when a new search is triggered from the SearchBar
. In StandardLayout
, add an onSearch
function that uses the React Router useSearchParams
hook to add the query
search parameter, delete it if the query is empty, and update the URL.
// src/pages/StandardLayout.tsx
import { SearchBar } from "@yext/search-ui-react";
import { useSearchParams } from "react-router-dom";
import { NavBar } from "../components/NavBar";
export interface StandardLayoutProps {
page: JSX.Element;
}
export const StandardLayout = ({ page }: StandardLayoutProps): JSX.Element => {
const [searchParams, setSearchParams] = useSearchParams();
const onSearch = (searchEventData: {
verticalKey?: string,
query?: string,
}) => {
const { query } = searchEventData;
if (query) {
searchParams.set("query", query);
} else {
searchParams.delete("query");
}
setSearchParams(searchParams);
};
return (
<>
<SearchBar onSearch={onSearch} />
<NavBar />
{page}
</>
);
};
Now, when a user searches, the query
param of the URL will update, but it won’t actually run a search. Instead, we want the app to rerun the search whenever the URL changes. For this, we’ll add a new usePageSetupEffect
hook that will re-trigger the search whenever the URL changes.
// src/hooks/usePageSetupEffect.tsx
import { useSearchActions } from "@yext/search-headless-react";
import { useEffect } from "react";
import { useLocation, useSearchParams } from "react-router-dom";
export const usePageSetupEffect = (verticalKey?: string) => {
const location = useLocation();
const [searchParams] = useSearchParams();
const searchActions = useSearchActions();
useEffect(() => {
searchActions.setQuery(searchParams.get("query") || "");
if (verticalKey) {
searchActions.setVertical(verticalKey);
searchActions.executeVerticalQuery();
} else {
searchActions.setUniversal();
searchActions.executeUniversalQuery();
}
}, [location, searchActions]);
};
Remove the search logic from the VerticalResultsPage
and UniversalResultsPage
and replace it with your custom hook.
// src/pages/VerticalResultsPage.tsx
import { StandardCard, VerticalResults } from "@yext/search-ui-react";
import { usePageSetupEffect } from "../hooks/usePageSetupEffect";
interface VerticalResultsPageProps {
verticalKey: string;
}
export const VerticalResultsPage = ({
verticalKey,
}: VerticalResultsPageProps): JSX.Element => {
usePageSetupEffect(verticalKey);
return <VerticalResults CardComponent={StandardCard} />;
};
// src/pages/UniversalResultsPage.tsx
import { UniversalResults } from "@yext/search-ui-react";
import { usePageSetupEffect } from "../hooks/usePageSetupEffect";
const universalResultsConfig = {
events: {
label: "Events",
},
locations: {
label: "Locations",
},
artists: {
label: "Artists",
},
};
export const UniversalResultsPage = (): JSX.Element => {
usePageSetupEffect();
return <UniversalResults verticalConfigMap={universalResultsConfig} />;
};
Now when you use the SearchBar
, the URL will update with the query and the usePageSetupEffect
hook will handle all the search logic.
Preserving Query Parameters
You may notice that when you click on the links in your NavBar
, the query
parameter disappears from the URL. You want for the query string to persist as you click on each vertical so that you can apply that query on a new search vertical. To fix this problem, add a LinkWithQuery
component that will preserve the existing search parameters in the URL when clicking on a link.
// src/components/LinkWithQuery.tsx
import { useEffect, useState } from "react";
import { Link, useLocation, useSearchParams } from "react-router-dom";
interface LinkWithQueryProps {
children: React.ReactNode;
to: string;
}
export const LinkWithQuery = ({
children,
to,
}: LinkWithQueryProps): JSX.Element => {
const [search, setSearch] = useState("");
const location = useLocation();
const [searchParams, setSearchParams] = useSearchParams();
useEffect(() => {
setSearch(
searchParams.has("query") ? `?query=${searchParams.get("query")}` : ""
);
}, [location]);
return <Link to={{ pathname: to, search }}>{children}</Link>;
};
Replace the Link
components in the renderLink
function in NavBar
with your LinkWithQuery
component.
// src/components/NavBar.tsx
const renderLink = (label: string, path: string): JSX.Element => {
return (
<LinkWithQuery key={`${path}_link`} to={path}>
<div
className={classNames(
"whitespace-nowrap py-3 mt-1 font-medium text-md border-b-2 hover:border-gray-300 border-transparent",
{
"text-blue-600 border-blue-600": isActiveLink(path),
}
)}
>
<div className="py-3 px-2">{label}</div>
</div>
</LinkWithQuery>
);
};
Now you can navigate between Verticals while preserving the query in the URL and you can copy and paste the URL into a new tab and get the same results as the original tab.