React router v6 history.listen



在React Router v5中有一个监听历史对象的方法。使用该方法,我编写了一个usePathname钩子,用于在路径更改时呈现组件。在React Router v6中,这个方法不再存在。这样的事情还有别的选择吗?我讨厌使用useLocation,因为如果状态改变,它也会呈现,而在这种情况下我不需要。

钩子在v5中使用。

import React from "react";
import { useHistory } from "react-router";
export function usePathname(): string {
let [state, setState] = React.useState<string>(window.location.pathname);
const history = useHistory();
React.useLayoutEffect(
() =>
history.listen((locationListener) => setState(locationListener.pathname)),
[history]
);
return state;
}

如上所述,只要当前位置发生变化,就可以使用useLocation执行副作用。

这是一个简单的typescript实现我的位置更改"listener"钩。应该可以帮助你得到你想要的结果

function useLocationEffect(callback: (location?: Location) => any) {
const location = useLocation();
useEffect(() => {
callback(location);
}, [location, callback]);
}
// usage:
useLocationEffect((location: Location) => 
console.log('changed to ' + location.pathname));

我现在使用这个代码

import { BrowserHistory } from "history";
import React, { useContext } from "react";
import { UNSAFE_NavigationContext } from "react-router-dom";
export default function usePathname(): string {
let [state, setState] = React.useState<string>(window.location.pathname);
const navigation = useContext(UNSAFE_NavigationContext)
.navigator as BrowserHistory;
React.useLayoutEffect(() => {
if (navigation) {
navigation.listen((locationListener) =>
setState(locationListener.location.pathname)
);
}
}, [navigation]);
return state;
}

It seems to work fine

我发现在React Rrouter v5中使用useNavigateuseLocationuseHistory相比毫无意义。

作为这些更改的结果,我做了一个瘦的自定义钩子来减轻我自己的任何重构。

只需重命名该钩子的导入路径,并使用"old"API与v6。要回答或只是提示你的问题-使用这种方法应该很容易实现listen函数在自定义钩子自己。

export function useHistory() {
const navigate = useNavigate();
const location = useLocation();
const listen = ...; // implement the hook yourself
return {
push: navigate,
go: navigate,
goBack: () => navigate(-1),
goForward: () => navigate(1),
listen,
location,
};
}

为什么不直接使用const { pathname } = useLocation();呢?如果状态改变,它确实会渲染,但在大多数情况下,这应该不是什么大问题。

如果你真的想避免这种行为,你可以创建一个自己的上下文来保存路径名:

// PathnameProvider.js
import React, { createContext, useContext } from 'react';
import { useLocation } from 'react-router';
const PathnameContext = createContext();
const PathnameProvider = ({ children }) => {
const { pathname } = useLocation();
return (
<PathnameContext.Provider value={pathname}>
{children}
</PathnameContext.Provider>
);
}
const usePathname = () => useContext(PathnameContext);
export { PathnameProvider as default, usePathname };

那么您可以在树的任何组件中使用usePathname()。只有当pathname实际改变时,它才会渲染。

考虑到@kryštof-Řeháček的建议(就在上面)是实现您自己的useListen钩子,但它可能不是很明显如何做到这一点,这里是我为自己实现的一个版本作为指导(注意:我还没有详尽的单元测试):

import { useState } from "react";
import { useLocation } from "react-router";
interface HistoryProps {
index: number;
isHistoricRoute: boolean;
key: string;
previousKey: string | null;
}
export const useHistory = (): HistoryProps => {

const { key } = useLocation();

const [history, setHistory] = useState<string[]>([]);
const [currentKey, setCurrentKey] = useState<string | null>(null);
const [previousKey, setPreviousKey] = useState<string | null>(null);
const contemporaneousHistory = history.includes(key)
? history
: [...history, key];
const index = contemporaneousHistory.indexOf(key);
const isHistoricRoute = index + 1 < contemporaneousHistory.length;
const state = { index, isHistoricRoute, key, previousKey };
if (history !== contemporaneousHistory) setHistory(contemporaneousHistory);
if (key !== currentKey) {
setPreviousKey(currentKey);
setCurrentKey(key);
}
return state;
}

我现在已经为react创建了一个新的路由库。

https://github.com/fast-router/fast-router

不支持服务器端呈现。其余的应该没问题。图书馆的设计灵感主要来自于wouter>https://github.com/molefrog/wouter有一些钩子,例如usePathname,它只在实际路径名改变时才会导致新的渲染(忽略散列和搜索)可以只选择历史记录的单个属性。状态,并且如果状态中的任何其他值发生变化,则不会获得新的渲染。

最新更新