Skip to content

Instantly share code, notes, and snippets.

@Tonyce
Created May 8, 2017 12:55
Show Gist options
  • Save Tonyce/d994b18ff700f6d2e524c1e24daaa4bd to your computer and use it in GitHub Desktop.
Save Tonyce/d994b18ff700f6d2e524c1e24daaa4bd to your computer and use it in GitHub Desktop.
Tyepscript + React Router v4
/**basicExample
import * as React from 'react';
import {
HashRouter as Router, Switch, Route, Link,
RouteComponentProps
} from 'react-router-dom';
const Home = () => (
<div>
<h2>Home</h2>
</div>
)
// 当然还可以这样 不过没有意义
// const Home = (
// <div>
// <h2>Home</h2>
// </div>
// )
class About extends React.Component<RouteComponentProps<{}>, {}> {
render() {
return (
<div>
<h2>About</h2>
</div>
)
}
}
class Topics extends React.Component<RouteComponentProps<{}>, {}> {
render() {
const { match } = this.props;
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/rendering`}>
Rendering with React
</Link>
</li>
<li>
<Link to={`${match.url}/components`}>
Components
</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>
Props v. State
</Link>
</li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic} />
<Route exact path={match.url} render={() => (
<h3>Please select a topic.</h3>
)} />
</div>
)
}
}
class Topic extends React.Component<RouteComponentProps<{ topicId: string }>, {}> {
render() {
const { match } = this.props;
return (
<div>
<h3>{match.params.topicId}</h3>
</div>
)
}
}
class BasicExample extends React.Component<{}, {}> {
render() {
return (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
)
}
}
export default BasicExample;
*/
/*
class Child extends React.Component<RouteComponentProps<{ id: string }>, {}> {
render() {
const { match } = this.props;
return (
<div>
<h3>ID: {match.params.id}</h3>
</div>
)
}
}
class ParamsExample extends React.Component<{}, {}> {
render() {
return (
<Router>
<div>
<h2>Accounts</h2>
<ul>
<li><Link to="/netflix">Netflix</Link></li>
<li><Link to="/zillow-group">Zillow Group</Link></li>
<li><Link to="/yahoo">Yahoo</Link></li>
<li><Link to="/modus-create">Modus Create</Link></li>
</ul>
<Route path="/:id" component={Child} />
</div>
</Router>
)
}
}
*/
/*
import * as React from 'react';
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter,
RouteComponentProps
} from 'react-router-dom';
const fakeAuth = {
isAuthenticated: false,
authenticate(cb: Function) {
this.isAuthenticated = true
setTimeout(cb, 100) // fake async
},
signout(cb: Function) {
this.isAuthenticated = false
setTimeout(cb, 100)
}
}
class AuthButtonT extends React.Component<RouteComponentProps<{}>, {}> {
render() {
const { history } = this.props;
return (
fakeAuth.isAuthenticated &&
<p>
Welcome! <button onClick={() => {
fakeAuth.signout(() => history.push('/'))
}}>Sign out</button>
</p>
|| <p>You are not logged in.</p>
)
}
}
const AuthButton = withRouter(AuthButtonT);
const Public = () => <h3>Public</h3>
const Protected = () => <h3>Protected</h3>
class PrivateRoute extends React.Component<{ path: string; Component: React.StatelessComponent<void> }, {}> {
render() {
const { path, Component } = this.props;
return (
<Route path={path} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}} />
)
)} />
)
}
}
class Login extends React.Component<RouteComponentProps<void>, { redirectToReferrer: boolean }> {
constructor() {
super()
this.state = {
redirectToReferrer: false
}
}
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true })
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (redirectToReferrer) {
return (
<Redirect to={from} />
)
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
)
}
}
export default class AuthExample extends React.Component<{}, {}> {
render() {
return (
<Router>
<div>
<AuthButton />
<ul>
<li><Link to="/public">Public Page</Link></li>
<li><Link to="/protected">Protected Page</Link></li>
</ul>
<Route path="/public" component={Public} />
<Route path="/login" component={Login} />
<PrivateRoute path="/protected" Component={Protected} />
</div>
</Router>
)
}
}
*/
/*
// custom link
import * as React from 'react';
import {
HashRouter as Router,
Route,
Link,
} from 'react-router-dom';
const Home = () => <div><h2>Home</h2></div>
const About = () => <div><h2>About</h2></div>
interface OldSchoolMenuLinkProps {
label: string;
to: string;
activeOnlyWhenExact?: boolean;
}
class OldSchoolMenuLink extends React.Component<OldSchoolMenuLinkProps, {}> {
render() {
const { activeOnlyWhenExact, to, label } = this.props;
return (
<Route path={to} exact={activeOnlyWhenExact} children={({ match }) => (
<div className={match ? 'active' : ''}>
{match ? '> ' : ''}<Link to={to}>{label}</Link>
</div>
)} />
)
}
}
export default class CustomLinkExample extends React.Component<{}, {}> {
render() {
return (
<Router>
<div>
<OldSchoolMenuLink activeOnlyWhenExact={true} to="/" label="Home" />
<OldSchoolMenuLink to="/about" label="About" />
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
</Router>
)
}
}
*/
/*
// Preventing Transitions Demo
import * as React from 'react'
import {
BrowserRouter as Router,
Route,
Link,
Prompt,
RouteComponentProps
} from 'react-router-dom'
interface FormState {
isBlocking: boolean;
}
class Form extends React.Component<RouteComponentProps<{}>, FormState> {
constructor(props: RouteComponentProps<{}>) {
super(props);
this.state = {
isBlocking: false
}
}
formSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
event.currentTarget.reset();
this.setState({
isBlocking: false
})
}
render() {
const { isBlocking } = this.state
return (
<form onSubmit={this.formSubmit}>
<Prompt
when={isBlocking}
message={location => (
`Are you sure you want to go to ${location.pathname}`
)}
/>
<p>
Blocking? {isBlocking ? 'Yes, click a link or the back button' : 'Nope'}
</p>
<p>
<input
size={50}
placeholder="type something to block transitions"
onChange={event => {
this.setState({
isBlocking: event.target.value.length > 0
})
}}
/>
</p>
<p>
<button>Submit to stop blocking</button>
</p>
</form>
)
}
}
export default class PreventingTransitionsExample extends React.Component<{}, {}> {
render() {
return (
<Router>
<div>
<ul>
<li><Link to="/">Form</Link></li>
<li><Link to="/one">One</Link></li>
<li><Link to="/two">Two</Link></li>
</ul>
<Route path="/" exact component={Form} />
<Route path="/one" render={() => <h3>One</h3>} />
<Route path="/two" render={() => <h3>Two</h3>} />
</div>
</Router>
);
}
}
*/
/*
import * as React from 'react';
import {
BrowserRouter as Router,
Route,
Link,
Switch,
Redirect, RouteComponentProps
} from 'react-router-dom';
const Home = () => (
<p>
A <code>&lt;Switch></code> renders the
first child <code>&lt;Route></code> that
matches. A <code>&lt;Route></code> with
no <code>path</code> always matches.
</p>
)
const WillMatch = () => <h3>Matched!</h3>
class NoMatch extends React.Component<RouteComponentProps<{}>, {}> {
render() {
return (
<div>
<h3>No match for <code>{location.pathname}</code></h3>
</div>
)
}
}
const NoMatchExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/old-match">Old Match, to be redirected</Link></li>
<li><Link to="/will-match">Will Match</Link></li>
<li><Link to="/will-not-match">Will Not Match</Link></li>
<li><Link to="/also/will/not/match">Also Will Not Match</Link></li>
</ul>
<Switch>
<Route path="/" exact component={Home} />
<Redirect from="/old-match" to="/will-match" />
<Route path="/will-match" component={WillMatch} />
<Route component={NoMatch} />
</Switch>
</div>
</Router>
)
export default NoMatchExample
*/
// type oo = number | string;
// type cc = boolean | string;
// var a: oo = null;
// var c: cc = '1';
// function qq(qq: oo): void {
// }
// qq(c);
// function dd(oo: cc): void {
// }
// dd(a)
/*
import * as React from 'react'
import {
// HashRouter as Router,
BrowserRouter as Router,
Route,
Link, RouteComponentProps
} from 'react-router-dom';
const PEEPS = [
{ id: 0, name: 'Michelle', friends: [1, 2, 3] },
{ id: 1, name: 'Sean', friends: [0, 3] },
{ id: 2, name: 'Kim', friends: [0, 1, 3], },
{ id: 3, name: 'David', friends: [1, 2] }
]
const find = (id: number) => PEEPS.find(p => p.id == id)
const RecursiveExample = () => (
<Router>
<Person match={{ params: { id: 0 }, url: '' }} />
<Route component={Person} />
</Router >
)
interface PersonProps {
match: {
params: { id: number },
url: string
}
}
class Person extends React.Component<RouteComponentProps<{ id: number }> | PersonProps, {}> {
// const Person = ({ match }) => {
render(): JSX.Element {
const { match } = this.props;
const { id } = match.params;
// console.log(id);
// let { url } = match;
// url = url == '/' ? '' : url
const person = find(id);
return (
<div>
<h3>{person.name}’s Friends</h3>
<ul>
{person.friends.map((id: number) => (
<li key={id}>
<Link to={`${match.url}/${id}`}>
{find(id).name}
</Link>
</li>
))}
</ul>
<Route path={`${url}/:id`} component={Person} />
<Route path={`${match.url}/:id`} render={(props: RouteComponentProps<{
id: number
}>) => {
return <Person {...props} />;
}} />
</div>
)
}
}
export default RecursiveExample
*/
/*
// sidebar
import * as React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
// Each logical "route" has two components, one for
// the sidebar and one for the main area. We want to
// render both of them in different places when the
// path matches the current URL.
const routes = [
{
path: '/',
exact: true,
sidebar: () => <div>home!</div>,
main: () => <h2>Home</h2>
},
{
path: '/bubblegum',
exact: true,
sidebar: () => <div>bubblegum!</div>,
main: () => <h2>Bubblegum</h2>
},
{
path: '/shoelaces',
exact: true,
sidebar: () => <div>shoelaces!</div>,
main: () => <h2>Shoelaces</h2>
}
]
const SidebarExample = () => (
<Router>
<div style={{ display: 'flex' }}>
<div style={{
padding: '10px',
width: '40%',
background: '#f0f0f0'
}}>
<ul style={{ listStyleType: 'none', padding: 0 }}>
<li><Link to="/">Home</Link></li>
<li><Link to="/bubblegum">Bubblegum</Link></li>
<li><Link to="/shoelaces">Shoelaces</Link></li>
</ul>
{routes.map((route, index) => (
// You can render a <Route> in as many places
// as you want in your app. It will render along
// with any other <Route>s that also match the URL.
// So, a sidebar or breadcrumbs or anything else
// that requires you to render multiple things
// in multiple places at the same URL is nothing
// more than multiple <Route>s.
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.sidebar}
/>
))}
</div>
<div style={{ flex: 1, padding: '10px' }}>
{routes.map((route, index) => (
// Render more <Route>s with the same paths as
// above, but different components this time.
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.main}
/>
))}
</div>
</div>
</Router>
)
export default SidebarExample
*/
import * as React from 'react';
import {
HashRouter as Router,
Switch,
Route,
Link, RouteComponentProps
} from 'react-router-dom';
const IMAGES = [
{ id: 0, title: 'Dark Orchid', color: 'DarkOrchid' },
{ id: 1, title: 'Lime Green', color: 'LimeGreen' },
{ id: 2, title: 'Tomato', color: 'Tomato' },
{ id: 3, title: 'Seven Ate Nine', color: '#789' },
{ id: 4, title: 'Crimson', color: 'Crimson' }
]
const Thumbnail = ({ color }: { color: string }) =>
<div style={{
width: 50,
height: 50,
background: color
}} />
const Image = ({ color }: { color: string }) =>
<div style={{
width: '100%',
height: 400,
background: color
}}></div>
const Home = () => (
<div>
<Link to='/gallery'>Visit the Gallery</Link>
<h2>Featured Images</h2>
<ul>
<li><Link to='/img/2'>Tomato</Link></li>
<li><Link to='/img/4'>Crimson</Link></li>
</ul>
</div>
)
const Gallery = () => (
<div>
{IMAGES.map(i => (
<Link
key={i.id}
to={{
pathname: `/img/${i.id}`,
// this is the trick!
state: { modal: true }
}}
>
<Thumbnail color={i.color} />
<p>{i.title}</p>
</Link>
))}
</div>
)
const ImageView = ({ match }: RouteComponentProps<{ id: string }>) => {
const image = IMAGES[parseInt(match.params.id, 10)]
if (!image) {
return <div>Image not found</div>
}
return (
<div>
<h1>{image.title}</h1>
<Image color={image.color} />
</div>
)
}
class Modal extends React.Component<RouteComponentProps<{ id: string }>, {}> {
back = (e: React.MouseEvent<HTMLDivElement | HTMLButtonElement>) => {
const { history } = this.props;
e.stopPropagation();
history.goBack();
}
render() {
const { match } = this.props;
const image = IMAGES[parseInt(match.params.id, 10)]
return (
<div
onClick={this.back}
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
background: 'rgba(0, 0, 0, 0.15)'
}}
>
<div className='modal' style={{
position: 'absolute',
background: '#fff',
top: 25,
left: '10%',
right: '10%',
padding: 15,
border: '2px solid #444'
}}>
<h1>{image.title}</h1>
<Image color={image.color} />
<button type='button' onClick={this.back}>
Close
</button>
</div>
</div>
)
}
}
// class ModalSwitch extends React.Component {
class ModalSwitch extends React.Component<RouteComponentProps<{}>, {}> {
// We can pass a location to <Switch/> that will tell it to
// ignore the router's current location and use the location
// prop instead.
//
// We can also use "location state" to tell the app the user
// wants to go to `/images/2` in a modal, rather than as the
// main page, keeping the gallery visible behind it.
//
// Normally, `/images/2` wouldn't match the gallery at `/`.
// So, to get both screens to render, we can save the old
// location and pass it to Switch, so it will think the location
// is still `/` even though its `/images/2`.
previousLocation = this.props.location
componentWillUpdate(nextProps: RouteComponentProps<{}>) {
const { location } = this.props
// set previousLocation if props.location is not modal
if (
nextProps.history.action !== 'POP' &&
(!location.state || !location.state.modal)
) {
this.previousLocation = this.props.location
}
}
render() {
const { location } = this.props
const isModal = !!(
location.state &&
location.state.modal &&
this.previousLocation !== location // not initial render
)
return (
<div>
<Switch location={isModal ? this.previousLocation : location}>
<Route exact path='/' component={Home} />
<Route path='/gallery' component={Gallery} />
<Route path='/img/:id' component={ImageView} />
</Switch>
{isModal ? <Route path='/img/:id' component={Modal} /> : null}
</div>
)
}
}
const ModalGallery = () => (
<Router>
<Route component={ModalSwitch} />
</Router>
)
export default ModalGallery
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment