Home > Software engineering >  Navigate to a search page after a search in the search bar and reuse the search bar within the searc
Navigate to a search page after a search in the search bar and reuse the search bar within the searc

Time:07-10

In my React application, at the layout level and in addition to the main content, I have a sidebar and a header with a profile section and a search bar - all different components.

The header goes with every page - routes - and I want to do searches on any route; after doing the search (sent with enter) I go to the "/search" route to display and filter the results.

This is a snippet of code related to the search bar component - form:

const SearchSection = () => {
  const [value, setValue] = useState("");

  const navigate = useNavigate();
  const location = useLocation();

  const handleSearch = (event) => {
    event.preventDefault();
    if (location.pathname !== "/search") {
      navigate("/search", { state: value });
    }
  };

  return (
    <>
      <Box
        component="form"
        onSubmit={handleSearch}
        sx={{ display: { xs: "none", md: "block" } }}
      >
        <OutlineInputStyle
          id="input-search-header"
          value={value}
          onChange={(e) => setValue(e.target.value)}
          placeholder="Search"
        />
      </Box>
    </>
  );
};

export default SearchSection;

After the user submits the search (with enter) I navigate to the search page and display the results:

const Search = () => {
  const [results, setResults] = useState([]);
  const [error, setError] = useState(null);

  const location = useLocation();

  function showResults() {
    const headers = { Accept: "application/ld json" };
    const response = fetch(
      `http://localhost:1026/ngsi-ld/v1/entities?type=Sensor&q=refRoom~=.*${location.state}`,
      {
        headers,
      }
    )
      .then((response) => {
        if (response.ok) {
          return response.json();
        }
        throw response;
      })
      .then((data) => {
        setResults(data);
      })
      .catch((error) => {
        console.error("Error fetching data: ", error);
        setError(error);
      });
  }

  useEffect(() => {
    showResults();
  }, []);

  return (
    <Grid container spacing={gridSpacing}>
      <Grid item xs={12}>
        <TableContainer component={Paper}>
          <Table>
            <TableHead>
              <TableRow>
                <TableCell>ID</TableCell>
                <TableCell>Type</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {results.map((result) => (
                <TableRow>
                  <TableCell>{result.id}</TableCell>
                  <TableCell>{result.type}</TableCell>
                </TableRow>
              ))}
            </TableBody>
          </Table>
        </TableContainer>
      </Grid>
    </Grid>
  );
};

export default Search;

routes/MainRoutes.js file:

import { lazy } from 'react';

import MainLayout from 'layout/MainLayout';
import Loadable from 'ui-component/Loadable';

const DashboardHome = Loadable(lazy(() => import('views/dashboard/Home')));
const SearchPage = Loadable(lazy(() => import('views/search')));

const MainRoutes = {
    path: '/',
    element: <MainLayout />,
    children: [
        {
            path: '/',
            element: <DashboardHome />
        },
        {
            path: '/search',
            element: <SearchPage />
        }
    ]
};

export default MainRoutes;

routes/index.js file:

import { useRoutes } from 'react-router-dom';
import MainRoutes from './MainRoutes';
import AuthenticationRoutes from './AuthenticationRoutes';

export default function ThemeRoutes() {
    return useRoutes([MainRoutes, AuthenticationRoutes], config.basename);
}

App.js file:

import { FirebaseProvider } from 'contexts/Firebase';
import Routes from 'routes';

const App = () => {
    return (
        <FirebaseProvider>
            <>
                <Routes />
            </>
        </FirebaseProvider>
    );
};

export default App;

The user can start the search on any route and, regardless of where he is, he is redirected to /search. The search bar is a subcomponent of the MainLayout component. The MainLayout component has the sidebar, header (which has the search bar) and main content, with the latter being the SearchPage component, DashboardHome, etc.

So far it works fine, the problem appears when I want to re-use the search bar already inside the /search route - it doesn't refresh the page.

How do I solve this problem and/or what is the best approach?

CodePudding user response:

Issues

  1. The SearchSection doesn't navigate to "/search" with the new search value when the URL path is already on `"/search".
  2. The SearchPage component doesn't "listen" to changes in the route state (from the location object) to invoke showResults with the new search parameters.

Solution

Just navigate to "/search" with new search state unconditionally. Or perhaps, only issue the navigation request if there is a search value. When the route state value changes, the useEffect hook callback will be called and issue the fetch request.

const SearchSection = () => {
  const [value, setValue] = useState("");

  const navigate = useNavigate();

  const handleSearch = (event) => {
    event.preventDefault();
    if (value) {
      navigate("/search", { state: value, replace: true });
    }
  };

  return (
    <>
      <Box
        component="form"
        onSubmit={handleSearch}
        sx={{ display: { xs: "none", md: "block" } }}
      >
        <OutlineInputStyle
          id="input-search-header"
          value={value}
          onChange={(e) => setValue(e.target.value)}
          placeholder="Search"
        />
      </Box>
    </>
  );
};

Unpack the route state and use as a dependency for the useEffect hook in SearchPage.

const Search = () => {
  const [results, setResults] = useState([]);
  const [error, setError] = useState(null);

  const { state } = useLocation(); // <-- unpack route state

  useEffect(() => {
    const headers = { Accept: "application/ld json" };

     // pass in queryString
    const url = `http://localhost:1026/ngsi-ld/v1/entities?type=Sensor&q=refRoom~=.*${state || ''}`;

    fetch(url, { headers })
      .then((response) => {
        if (response.ok) {
          return response.json();
        }
        throw response;
      })
      .then((data) => {
        setResults(data);
      })
      .catch((error) => {
        console.error("Error fetching data: ", error);
        setError(error);
      });
  }, [state]); // <-- use as effect dependency

  return (
    ...
  );
};
  • Related