I want to attach a computed value of query string into URL whenever user navigates with history.push
method.
here is my code
let pusher: null | ((a: unknown, b?: unknown) => void) = null;
/**
* This hook will push a hashed value of query string of the url
*
* Don't mind about memory leak, it's `Singletom`
* @param history an instance of History from useHistory
* @returns `Pusher` an enhance version of `History.push`
*/
export default function useHashPusher(history: ReturnType<typeof useHistory>){
const sha256 = useSha256();
if(!pusher) {
pusher = function(...args) {
const data = args[0];
if(typeof data === 'object' && data && 'search' in data){
const raw = data['search' as keyof typeof data];
if(raw){
const hashed = sha256.hash(raw);
(data['search' as keyof typeof data] as string) = `${raw}&hash=${hashed}`;
console.log(data);
}
}
console.log('overrided!', args[0]);
history.push(...args);
};
}
return pusher;
}
It works OK but I have to update code at many place from history.push
to my pusher
Is there any way to override the history.push
method? I'm using react-router-dom v5
CodePudding user response:
It looks like my commented suggestion does work. In a separate file create and export a custom history
object.
Example:
import { createBrowserHistory, History } from "history";
import { sha256 } from "../path/to/sha256"; // *
const historyBase = createBrowserHistory();
const history: History = {
...historyBase,
push: (...args) => {
const data = args[0];
if (typeof data === "object" && data && "search" in data) {
const raw = data["search" as keyof typeof data];
if (raw) {
const hashed = sha256.hash(raw); // *
(data[
"search" as keyof typeof data
] as string) = `${raw}&hash=${hashed}`;
console.log(data);
}
}
console.log("overrided!", args[0]);
historyBase.push(...args);
}
};
export default history;
* Note: The useSha256
hook won't work here for this, but since I wasn't able to find any NPM package that provided this hook I am assuming this is a local custom hook. If this is the case then import the object the hook was previously.
Import the low-level base Router
component from react-router-dom
and your custom history
object and pass history
as a prop.
Example:
import { StrictMode } from "react";
import * as ReactDOMClient from "react-dom/client";
import { Router } from "react-router-dom";
import history from './history';
import App from "./App";
const rootElement = document.getElementById("root");
const root = ReactDOMClient.createRoot(rootElement);
root.render(
<StrictMode>
<Router history={history}>
<App />
</Router>
</StrictMode>
);
At this point the router is using your history
object in it's context, so all navigation actions will use it. This means not only directly accessing history
in nested children, but Link
and other components that take a To
object argument as well.
Example:
import { Link, useHistory } from "react-router-dom";
export default function App() {
const history = useHistory();
const handler = () => {
history.push({
pathname: "/test",
search: "?....."
});
};
return (
<div className="App">
...
<ul>
...
<li>
<Link to={{ pathname: "/foo", search: "?....." }}>
Foo
</Link>
</li>
</ul>
<button type="button" onClick={handler}>
Navigate
</button>
</div>
);
}