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
- The
SearchSectiondoesn't navigate to"/search"with the new search value when the URL path is already on `"/search". - The
SearchPagecomponent doesn't "listen" to changes in the route state (from thelocationobject) to invokeshowResultswith 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 (
...
);
};
