Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/routeState: Route state for the react-resource-router #103

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions examples/routing-with-route-state/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>

<head>
<title>Example - Routing with route state</title>
</head>

<body>
<div id="root"></div>
<script src="bundle.js" type="text/javascript"></script>
</body>

</html>
29 changes: 29 additions & 0 deletions examples/routing-with-route-state/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import ReactDOM from 'react-dom';

import {
Router,
RouteComponent,
createBrowserHistory,
} from 'react-resource-router';

import { stateProviderRoute, stateConsumerRoute, stateConsumerWithRedirectionRoute } from './routes';

const myHistory = createBrowserHistory();

const appRoutes = [stateProviderRoute, stateConsumerRoute, stateConsumerWithRedirectionRoute];

const App = () => {
return (
<Router
routes={appRoutes}
history={myHistory}
basePath="/routing-with-route-state"
onPrefetch={({ route }) => console.log('Prefetcing route', route.name)}
>
<RouteComponent />
</Router>
);
};

ReactDOM.render(<App />, document.getElementById('root'));
31 changes: 31 additions & 0 deletions examples/routing-with-route-state/routes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { StateConsumer } from './state-consumer';
import { StateProvider } from './state-provider';
import { StateConsumerWithRedirection } from './state-consumer-with-redirection';

export const stateProviderRoute = {
name: 'provider',
path: '/',
exact: true,
component: StateProvider,
navigation: null,
resources: [],
};

export const stateConsumerRoute = {
name: 'consumer',
path: '/consumer',
exact: true,
component: StateConsumer,
navigation: null,
resources: [],
};

export const stateConsumerWithRedirectionRoute = {
name: 'consumer-with-redirection',
path: '/redirector',
exact: true,
component: StateConsumerWithRedirection,
navigation: null,
resources: [],
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import {
Redirect
} from 'react-resource-router';


export const StateConsumerWithRedirection = () => {
return (
<Redirect to={{
pathname: '/consumer',
state: { message: 'Hey! It was from <Redirect />' }
}} />
);
};
17 changes: 17 additions & 0 deletions examples/routing-with-route-state/state-consumer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import {
useRouter
} from 'react-resource-router';


export const StateConsumer = () => {
const [{ location: { state = {} } }, { goBack, push }] = useRouter();
const { message, replaced } = state as any;

return (
<>
<button type="button" onClick={() => replaced ? push('/') : goBack()}>go back</button>
<p>{message}</p>
</>
);
};
29 changes: 29 additions & 0 deletions examples/routing-with-route-state/state-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import {
Link,
useRouterActions
} from 'react-resource-router';


export const StateProvider = () => {
const { push, replace } = useRouterActions();
return (
<>
<h1>There is a several variants presented to pass some state options to the next route</h1>
<button type="button" onClick={() => {
push('/consumer', { message: "Aloha from push()" })
}}>Use PUSH method</button>
<br />
<button type="button" onClick={() => {
replace('/consumer', { message: "Greatings from replace()", replaced: true })
}}>Use REPLACE method</button>
<br />
<Link to={{
pathname: '/consumer',
state: { message: 'Hola! It was from <Link />' }
}}>Use LINK component</Link>
<br />
<Link to="/redirector">Use LINK to REDIRECT component</Link>
</>
);
};
16 changes: 9 additions & 7 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@ import {
AnchorHTMLAttributes,
} from 'react';

import { History, Location as HistoryLocationShape } from 'history';
import { History, Location as HistoryLocationShape, LocationState } from 'history';

export type LocationShape = HistoryLocationShape;

export type Href = string;

export type Location = {
pathname: string;
search: string;
hash: string;
search?: string;
hash?: string;
state?: LocationState;
};

export type BrowserHistory = Omit<
History,
'location' | 'go' | 'createHref' | 'push' | 'replace'
> & {
location: Location;
push: (path: string) => void;
replace: (path: string) => void;
push: (path: string, state?: unknown) => void;
replace: (path: string, state?: unknown) => void;
};

export type MatchParams = {
Expand Down Expand Up @@ -109,7 +110,7 @@ export type RouteResourceResponseLoaded<RouteResourceData> = {

export type RouteResourceResponse<
RouteResourceData = unknown
> = RouteResourceResponseBase<RouteResourceData> &
> = RouteResourceResponseBase<RouteResourceData> &
(
| RouteResourceResponseLoading<RouteResourceData>
| RouteResourceResponseError<RouteResourceData>
Expand Down Expand Up @@ -237,7 +238,7 @@ export type LinkProps = AnchorHTMLAttributes<HTMLAnchorElement> & {
children: ReactNode;
target?: '_blank' | '_self' | '_parent' | '_top';
href?: string;
to?: string | Route | Promise<{ default: Route } | Route>;
to?: string | Route | Location | Promise<{ default: Route }>;
replace?: boolean;
type?: 'a' | 'button';
onClick?: (e: MouseEvent | KeyboardEvent) => void;
Expand Down Expand Up @@ -291,6 +292,7 @@ export type GenerateLocationOptions = {
params?: MatchParams;
query?: Query;
basePath?: string;
state?: LocationState;
};

export type CreateRouterContextOptions = {
Expand Down
3 changes: 2 additions & 1 deletion src/common/utils/generate-location/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function generateLocationFromPath(
pattern = '/',
options: GenerateLocationOptions = {}
): Location {
const { params = {}, query = {}, basePath = '' } = options;
const { params = {}, query = {}, basePath = '', state } = options;
// @ts-ignore stringify accepts two params but it's type doesn't say so
const stringifiedQuery = qs.stringify(query, true);
const pathname =
Expand All @@ -17,5 +17,6 @@ export function generateLocationFromPath(
pathname: `${basePath}${pathname}`,
search: stringifiedQuery,
hash: '',
state
};
}
20 changes: 10 additions & 10 deletions src/common/utils/history/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const methodsPlaceholders = {
block: () => noop,
};

const getLocation = () => {
const getLocation = (): Location => {
// todo - don't force non-optional search and hash
const { pathname = '', search = '', hash = '' } =
(hasWindow() && window.location) || {};
Expand Down Expand Up @@ -107,15 +107,15 @@ export const createLegacyHistory = (): BrowserHistory => {
},
...(hasWindow()
? {
push: (path: string) => window.location.assign(path),
replace: (path: string) =>
window.history.replaceState({}, document.title, path),
goBack: () => window.history.back(),
goForward: () => window.history.forward(),
listen: createLegacyListener(updateExposedLocation),
block: () => noop,
createHref: (location: Location) => createPath(location),
}
push: (path: string) => window.location.assign(path),
replace: (path: string) =>
window.history.replaceState({}, document.title, path),
goBack: () => window.history.back(),
goForward: () => window.history.forward(),
listen: createLegacyListener(updateExposedLocation),
block: () => noop,
createHref: (location: Location) => createPath(location),
}
: methodsPlaceholders),
};
};
2 changes: 1 addition & 1 deletion src/common/utils/router-context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const findRouterContext = (
): RouterContext => {
const { location, basePath = '' } = options;
const { pathname, search } = location;
const query = qs.parse(search) as Query;
const query = search ? qs.parse(search) as Query : {};
const matchedRoute = matchRoute(routes, pathname, query, basePath);

return {
Expand Down
12 changes: 6 additions & 6 deletions src/controllers/hooks/router-store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ const createPathParamHook = createHook<
export const useQueryParam = (
paramKey: string
): [
string | undefined,
(newValue: string | undefined, updateType?: HistoryUpdateType) => void
] => {
string | undefined,
(newValue: string | undefined, updateType?: HistoryUpdateType) => void
] => {
const [paramVal, routerActions] = createQueryParamHook({ paramKey });

const setQueryParam = React.useCallback(
Expand All @@ -77,9 +77,9 @@ export const useQueryParam = (
export const usePathParam = (
paramKey: string
): [
string | undefined,
(newValue: string | undefined, updateType?: HistoryUpdateType) => void
] => {
string | undefined,
(newValue: string | undefined, updateType?: HistoryUpdateType) => void
] => {
const [paramVal, routerActions] = createPathParamHook({ paramKey });

const setPathParam = React.useCallback(
Expand Down
3 changes: 2 additions & 1 deletion src/controllers/redirect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Redirector extends Component<RedirectorProps> {
const newPath = typeof to === 'object' ? createPath(to) : to;
const currentPath = createPath(location);
const action = push ? actions.push : actions.replace;
const state = to && typeof to !== 'string' ? to.state : undefined;

if (currentPath === newPath) {
if (
Expand All @@ -37,7 +38,7 @@ class Redirector extends Component<RedirectorProps> {
return;
}

action(newPath);
action(newPath, state);
}

render() {
Expand Down
8 changes: 4 additions & 4 deletions src/controllers/router-store/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,12 @@ const actions: AllRouterActions = {
});
},

push: path => ({ getState }) => {
push: (path, state) => ({ getState }) => {
const { history, basePath } = getState();
if (isExternalAbsolutePath(path)) {
window.location.assign(path as string);
} else {
history.push(getRelativePath(path, basePath) as any);
history.push(getRelativePath(path, basePath) as any, state);
}
},

Expand All @@ -247,12 +247,12 @@ const actions: AllRouterActions = {
history.push(location as any);
},

replace: path => ({ getState }) => {
replace: (path, state) => ({ getState }) => {
const { history, basePath } = getState();
if (isExternalAbsolutePath(path)) {
window.location.replace(path as string);
} else {
history.replace(getRelativePath(path, basePath) as any);
history.replace(getRelativePath(path, basePath) as any, state);
}
},

Expand Down
4 changes: 2 additions & 2 deletions src/controllers/router-store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ type PrivateRouterActions = {
};

type PublicRouterActions = {
push: (path: Href, state?: any) => RouterAction;
push: (path: Href, state?: unknown) => RouterAction;
pushTo: (route: Route, attributes?: ToAttributes) => RouterAction;
replace: (path: Href) => RouterAction;
replace: (path: Href, state?: unknown) => RouterAction;
replaceTo: (route: Route, attributes?: ToAttributes) => RouterAction;
goBack: () => RouterAction;
goForward: () => RouterAction;
Expand Down
Loading