Skip to content

Commit 3810a82

Browse files
committed
feat: create go() and <Link>
1 parent 52a1fde commit 3810a82

File tree

6 files changed

+96
-6
lines changed

6 files changed

+96
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const MyComponent = mock();
4646
- Generators
4747
- [`<Audio>`](./docs/Audio.md)
4848
- [`<LocalStorage>`](./docs/LocalStorage.md)
49+
- `<Redirect>`
4950
- [`<Speak>`](./docs/Speak.md)
5051
- [`<Vibrate>`](./docs/Vibrate.md)
5152
- `<Video>`

docs/route.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,18 @@ so, simply use:
5757

5858
## Multiple routers
5959

60-
You can have many routers operating on the same page in parallel. All you have to do is specify a *namespace* using the `ns` props.
60+
You can have many routers operating on the same page in parallel. All you have to do is specify a *namespace* using the `ns` prop.
6161

6262
```jsx
6363
<Router ns='secret'>
6464
<Route ns='secret' />
6565
</Router>
6666
```
6767

68+
This allows you to route by basically anything, not just the current page location. You can have *app-inside-app* that has its
69+
own routing logic.
70+
71+
6872
## Reference
6973

7074
### `<Router>`
@@ -139,6 +143,27 @@ interface TRouteMatchResult {
139143
- `matches` - optionsl, array of matches returned by `String.prototype.match()` function.
140144

141145

146+
### `go()`
147+
148+
Utility function that changes the current browser location. Has the following signature:
149+
150+
```ts
151+
go: (url: string, params?: IGoParams) => void;
152+
153+
interface IGoParams {
154+
replace?: boolean;
155+
title?: string;
156+
state?: any;
157+
}
158+
```
159+
160+
- `url` - required, string, URL where to navigate the browser. Usually you want to use a relative route with leading slash, like `/users`.
161+
- `replace` - whether to to use [`.replaceState()`](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_replaceState()_method)
162+
rather than default [`.pushState()`](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState()_method) when invoking History API.
163+
- `title` - title to specify in `.pushState()` or `.replaceState()` methods.
164+
- `state` - any serializable JavaScript object to store with the current history state. Useful, for example, to store current scroll position.
165+
166+
142167
## Example
143168

144169
### With Redux

src/route/Link.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Props to: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/Link.js
2+
import {Component} from 'react';
3+
import {h} from '../util';
4+
import {go} from '.';
5+
6+
const isModifiedEvent = (event) =>
7+
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
8+
9+
export interface ILinkProps {
10+
onClick?: (event) => void;
11+
target?: string;
12+
replace?: boolean;
13+
state?: any;
14+
to?: string;
15+
innerRef?: (el) => void;
16+
}
17+
18+
export class Link extends Component<ILinkProps, any> {
19+
onClick = (event) => {
20+
const {onClick, replace, target, to, state} = this.props;
21+
22+
if (onClick) {
23+
onClick(event);
24+
}
25+
26+
if (!event.defaultPrevented && // onClick prevented default
27+
event.button === 0 && // ignore everything but left clicks
28+
!target && // let browser handle "target=_blank" etc.
29+
!isModifiedEvent(event) // ignore clicks with modifier keys
30+
) {
31+
event.preventDefault();
32+
33+
go(to, {
34+
replace,
35+
state: typeof state === 'function' ? state() : state
36+
});
37+
}
38+
};
39+
40+
render () {
41+
const {props, onClick} = this;
42+
const {replace, to, innerRef: ref, ...rest} = this.props;
43+
44+
return h('a', {
45+
...rest,
46+
href: to,
47+
ref,
48+
onClick,
49+
});
50+
}
51+
}

src/route/__story__/StoryRouteExample2.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import {h} from '../../util';
2-
import {Router, Route} from '..';
2+
import {Router, Route, go} from '..';
3+
import {Link} from '../Link';
34

45
const StoryRouteExample2 = () => (
56
<Router>
67
<div>
78
<ul>
8-
<li><a onClick={() => history.pushState(null, '', '/')}>Home</a></li>
9-
<li><a onClick={() => history.pushState(null, '', '/about')}>About</a></li>
10-
<li><a onClick={() => history.pushState(null, '', '/topics')}>Topics</a></li>
9+
<li><a onClick={() => go('/')}>Home</a></li>
10+
<li><Link to='/about'>About</Link></li>
11+
<li><Link to='/topics'>Topics</Link></li>
1112
</ul>
1213

1314
<hr/>

src/route/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,15 @@ export class Route extends Component<IRouteMatch, any> {
129129
});
130130
}
131131
}
132+
133+
export interface IGoParams {
134+
replace?: boolean;
135+
title?: string;
136+
state?: any;
137+
}
138+
139+
export type TGo = (url: string, params: IGoParams) => void;
140+
141+
export const go = (url, {replace, title = '', state}: IGoParams = {}) => {
142+
history[replace ? 'replaceState' : 'pushState'](state, title || '', url);
143+
};

src/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const on = (obj, ...args) => obj.addEventListener(...args);
2828

2929
export const off = (obj, ...args) => obj.removeEventListener(...args);
3030

31-
export const ns = (name) => `@@${name}`;
31+
export const ns = (name) => `@@libreact/${name}`;
3232

3333
const hasSymbols = typeof Symbol !== 'undefined';
3434

0 commit comments

Comments
 (0)