inula-router问题修复及优化
This commit is contained in:
parent
1af8f0271a
commit
1f0dbaa180
|
@ -16,7 +16,7 @@
|
|||
import Inula from 'openinula';
|
||||
import { useLayoutEffect, useRef, reduxAdapter, InulaNode } from 'openinula';
|
||||
import { connect, ReactReduxContext } from 'react-redux';
|
||||
import { Store } from 'redux';
|
||||
import type { Store } from 'redux';
|
||||
import { History, Location, Router } from '../router';
|
||||
import { Action, DefaultStateType, Navigation } from '../history/types';
|
||||
import { ActionMessage, onLocationChanged } from './actions';
|
||||
|
@ -130,9 +130,14 @@ function getConnectedRouter(type: StoreType) {
|
|||
);
|
||||
};
|
||||
|
||||
const ConnectedHRouterWithContext = (props: any) => {
|
||||
const { store, ...rest } = props;
|
||||
return <ConnectedRouter store={store} storeType={type} {...rest} />;
|
||||
};
|
||||
|
||||
// 针对不同的Store类型,使用对应的connect函数
|
||||
if (type === 'InulaXCompat') {
|
||||
return hConnect(null as any, mapDispatchToProps)(ConnectedRouterWithContext as any);
|
||||
return hConnect(null as any, mapDispatchToProps)(ConnectedHRouterWithContext as any);
|
||||
}
|
||||
if (type === 'Redux') {
|
||||
return connect(null, mapDispatchToProps)(ConnectedRouterWithContext);
|
||||
|
|
|
@ -107,7 +107,7 @@ export function createHashHistory<S = DefaultStateType>(option: HashHistoryOptio
|
|||
warning(state !== undefined, 'Hash history does not support state, it will be ignored');
|
||||
|
||||
const action = Action.push;
|
||||
const location = createLocation<S>(history.location, to, undefined, '');
|
||||
const location = createLocation<S>(history.location, to, state, '');
|
||||
|
||||
transitionManager.confirmJumpTo(location, action, getUserConfirmation, isJump => {
|
||||
if (!isJump) {
|
||||
|
@ -132,7 +132,7 @@ export function createHashHistory<S = DefaultStateType>(option: HashHistoryOptio
|
|||
function replace(to: To, state?: S) {
|
||||
warning(state !== undefined, 'Hash history does not support state, it will be ignored');
|
||||
const action = Action.replace;
|
||||
const location = createLocation<S>(history.location, to, undefined, '');
|
||||
const location = createLocation<S>(history.location, to, state, '');
|
||||
|
||||
transitionManager.confirmJumpTo(location, action, getUserConfirmation, isJump => {
|
||||
if (!isJump) {
|
||||
|
|
|
@ -28,8 +28,8 @@ export function createPath(path: Partial<Path>): string {
|
|||
}
|
||||
|
||||
export function parsePath(url: string): Partial<Path> {
|
||||
let pathname = url || '/';
|
||||
const parsedPath: Partial<Path> = {
|
||||
pathname: url || '/',
|
||||
search: '',
|
||||
hash: '',
|
||||
};
|
||||
|
@ -38,16 +38,16 @@ export function parsePath(url: string): Partial<Path> {
|
|||
if (hashIdx > -1) {
|
||||
const hash = url.substring(hashIdx);
|
||||
parsedPath.hash = hash === '#' ? '' : hash;
|
||||
url = url.substring(0, hashIdx);
|
||||
pathname = pathname.substring(0, hashIdx);
|
||||
}
|
||||
|
||||
const searchIdx = url.indexOf('?');
|
||||
if (searchIdx > -1) {
|
||||
const search = url.substring(searchIdx);
|
||||
parsedPath.search = search === '?' ? '' : search;
|
||||
url = url.substring(0, searchIdx);
|
||||
pathname = pathname.substring(0, searchIdx);
|
||||
}
|
||||
parsedPath.pathname = url;
|
||||
parsedPath.pathname = pathname;
|
||||
return parsedPath;
|
||||
}
|
||||
|
||||
|
@ -116,12 +116,12 @@ export function stripBasename(path: string, prefix: string): string {
|
|||
export function createMemoryRecord<T, S>(initVal: S, fn: (arg: S) => T) {
|
||||
let visitedRecord: T[] = [fn(initVal)];
|
||||
|
||||
function getDelta(to: S, form: S): number {
|
||||
let toIdx = visitedRecord.lastIndexOf(fn(to));
|
||||
function getDelta(toKey: S, formKey: S): number {
|
||||
let toIdx = visitedRecord.lastIndexOf(fn(toKey));
|
||||
if (toIdx === -1) {
|
||||
toIdx = 0;
|
||||
}
|
||||
let fromIdx = visitedRecord.lastIndexOf(fn(form));
|
||||
let fromIdx = visitedRecord.lastIndexOf(fn(formKey));
|
||||
if (fromIdx === -1) {
|
||||
fromIdx = 0;
|
||||
}
|
||||
|
|
|
@ -24,27 +24,42 @@ import { parsePath } from '../history/utils';
|
|||
|
||||
type NavLinkProps = {
|
||||
to: Partial<Location> | string | ((location: Location) => string | Partial<Location>);
|
||||
isActive?: (match: Matched | null, location: Location) => boolean;
|
||||
isActive?<P extends { [K in keyof P]?: string }>(match: Matched<P> | null, location: Location): boolean;
|
||||
exact?: boolean;
|
||||
strict?: boolean;
|
||||
sensitive?: boolean;
|
||||
className?: string | ((isActive: boolean) => string);
|
||||
activeClassName?: string;
|
||||
[key: string]: any;
|
||||
} & LinkProps;
|
||||
} & Omit<LinkProps, 'className'>;
|
||||
|
||||
type Page = 'page';
|
||||
|
||||
function NavLink<P extends NavLinkProps>(props: P) {
|
||||
const { to, isActive, ...rest } = props;
|
||||
const { to, isActive, exact, strict, sensitive, className, activeClassName, ...rest } = props;
|
||||
const context = useContext(Context);
|
||||
|
||||
const toLocation = typeof to === 'function' ? to(context.location) : to;
|
||||
|
||||
const { pathname } = typeof toLocation === 'string' ? parsePath(toLocation) : toLocation;
|
||||
|
||||
const match = pathname ? matchPath(context.location.pathname, pathname) : null;
|
||||
const match = pathname ? matchPath(context.location.pathname, pathname, {
|
||||
exact: exact,
|
||||
strictMode: strict,
|
||||
caseSensitive: sensitive,
|
||||
}) : null;
|
||||
|
||||
const isLinkActive = match && isActive ? isActive(match, context.location) : false;
|
||||
const isLinkActive = !!(isActive ? isActive(match, context.location) : match);
|
||||
|
||||
let classNames = typeof className === 'function' ? className(isLinkActive) : className;
|
||||
if (isLinkActive) {
|
||||
classNames = [activeClassName, classNames].filter(Boolean).join('');
|
||||
}
|
||||
|
||||
const page: Page = 'page';
|
||||
const otherProps = {
|
||||
'aria-current': isLinkActive ? page : false,
|
||||
className: classNames,
|
||||
'aria-current': isLinkActive ? page : undefined,
|
||||
...rest,
|
||||
};
|
||||
|
||||
|
|
|
@ -43,15 +43,19 @@ export type RouteProps<P extends Record<string, any> = {}, Path extends string =
|
|||
function Route<Path extends string, P extends Record<string, any> = GetURLParams<Path>>(props: RouteProps<P, Path>) {
|
||||
const context = useContext(RouterContext);
|
||||
|
||||
const { computed, location, path } = props;
|
||||
let { children, component, render } = props;
|
||||
const { computed, location, path, component, render, strict, sensitive, exact } = props;
|
||||
let { children } = props;
|
||||
let match: Matched<P> | null;
|
||||
|
||||
const routeLocation = location || context.location;
|
||||
if (computed) {
|
||||
match = computed;
|
||||
} else if (path) {
|
||||
match = matchPath<P>(routeLocation.pathname, path);
|
||||
match = matchPath<P>(routeLocation.pathname, path, {
|
||||
strictMode: strict,
|
||||
caseSensitive: sensitive,
|
||||
exact: exact,
|
||||
});
|
||||
} else {
|
||||
match = context.match;
|
||||
}
|
||||
|
|
|
@ -29,22 +29,27 @@ function Router<P extends RouterProps>(props: P) {
|
|||
const { history, children = null } = props;
|
||||
const [location, setLocation] = useState(props.history.location);
|
||||
const pendingLocation = useRef<Location | null>(null);
|
||||
const unListen = useRef<null | (() => void)>(null);
|
||||
const isMount = useRef<boolean>(false);
|
||||
|
||||
// 在Router加载时就监听history地址变化,以保证在始渲染时重定向能正确触发
|
||||
const unListen = useRef<null | (() => void)>(
|
||||
history.listen(arg => {
|
||||
if (unListen.current === null) {
|
||||
unListen.current = history.listen(arg => {
|
||||
pendingLocation.current = arg.location;
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟componentDidMount和componentWillUnmount
|
||||
useLayoutEffect(() => {
|
||||
isMount.current = true;
|
||||
if (unListen.current) {
|
||||
unListen.current();
|
||||
}
|
||||
// 监听history中的位置变化
|
||||
unListen.current = history.listen(arg => {
|
||||
setLocation(arg.location);
|
||||
if (isMount.current) {
|
||||
setLocation(arg.location);
|
||||
}
|
||||
});
|
||||
|
||||
if (pendingLocation.current) {
|
||||
|
@ -53,6 +58,7 @@ function Router<P extends RouterProps>(props: P) {
|
|||
|
||||
return () => {
|
||||
if (unListen.current) {
|
||||
isMount.current = false;
|
||||
unListen.current();
|
||||
unListen.current = null;
|
||||
pendingLocation.current = null;
|
||||
|
|
|
@ -40,7 +40,7 @@ export type Matched<P = any> = {
|
|||
|
||||
const defaultOption: Required<ParserOption> = {
|
||||
// url匹配时是否大小写敏感
|
||||
caseSensitive: true,
|
||||
caseSensitive: false,
|
||||
// 是否严格匹配url结尾的/
|
||||
strictMode: false,
|
||||
// 是否完全精确匹配
|
||||
|
|
|
@ -20,10 +20,11 @@ import RouterContext from './context';
|
|||
function withRouter<C extends ComponentType>(Component: C) {
|
||||
|
||||
function ComponentWithRouterProp(props: any) {
|
||||
const { wrappedComponentRef, ...rest } = props;
|
||||
const { history, location, match } = useContext(RouterContext);
|
||||
const routeProps = { history: history, location: location, match: match };
|
||||
|
||||
return <Component {...props} {...routeProps} />;
|
||||
return <Component {...routeProps} {...rest} ref={wrappedComponentRef} />;
|
||||
}
|
||||
|
||||
return ComponentWithRouterProp;
|
||||
|
|
Loading…
Reference in New Issue