Router
import React, { useCallback, useState, useEffect, createContext, useMemo } from 'react';
import { createBrowserHistory as createHistory, History } from 'history';
export const RouterContext = createContext(null);
export let rootHistory = null;
export default function Router(props) {
const history: History = useMemo(() => {
rootHistory = createHistory();
return rootHistory;
}, []);
const [location, setLocation] = useState(history.location);
useEffect(() => {
const unListen = history.listen((location) => {
setLocation(location);
});
return function () {
unListen?.();
};
});
return <RouterContext.Provider
value={{
location,
history,
match: {
path: '/',
url: '/',
params: {},
isExact: location.pathname === '/',
}
}}
>
{ props.children }
</RouterContext.Provider>;
}
Route
import React, { useContext } from 'react';
import { matchPath } from 'react-router';
import { RouterContext } from './Router';
export default function Route(props: any) {
const context: any = useContext(RouterContext);
const location = props.location || context.location;
const match = props.computedMatch ? props.computedMatch : (props.path ? matchPath(location.pathname, props) : context.match);
const newRouterProps = { ...context, location, match };
let { children, component, render } = props;
if (Array.isArray(children) && children.length === 0) {
children = null;
}
let renderChildren = null;
if (newRouterProps.match) {
if (children) {
renderChildren = typeof children === 'function' ? children(newRouterProps): children;
} else if (component) {
renderChildren = React.createElement(component, newRouterProps);
} else if (render) {
renderChildren = render(newRouterProps);
}
}
return <RouterContext.Provider value={ newRouterProps }>
{ renderChildren }
</RouterContext.Provider>;
}
Switch
import React, { useContext } from 'react';
import { matchPath } from 'react-router';
import { RouterContext} from './Router';
export default function Switch(props) {
const context: any = useContext(RouterContext);
const location = props.location || context.location;
let children, match;
React.Children.forEach(props.children, child => {
if (!match && React.isValidElement(child)) {
const path = child.props.path;
children = child;
match = path ? matchPath(location.pathname, {...child.props}) : context.match;
}
});
return match ? React.cloneElement(children, {
location: location,
computedMatch: match
}) : null;
}
withRouter
import React , { useContext } from 'react'
import hoistStatics from 'hoist-non-react-statics';
import { RouterContext } from './Router';
export default function withRouter(Component) {
const WrapComponent = (props) => {
const { wrappedComponentRef, ...otherProps } = props;
const context = useContext(RouterContext);
return <Component
{ ...otherProps }
ref={ wrappedComponentRef }
{ ...context }
/>
};
return hoistStatics(WrapComponent, Component);
}
hook
import { useContext } from 'react';
import { RouterContext } from './Router';
export function useHistory() {
return useContext(RouterContext).history;
}
export function useHistory() {
return useContext(RouterContext).location;
}