8000 Implemented react-router v4 to ReactRedux template · aspnet/JavaScriptServices@785e7d4 · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Commit 785e7d4

Browse files
KevenvzSteveSandersonMS
authored andcommitted
Implemented react-router v4 to ReactRedux template
1 parent c791cee commit 785e7d4

File tree

9 files changed

+67
-69
lines changed

9 files changed

+67
-69
lines changed

templates/ReactReduxSpa/ClientApp/boot-client.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@ import './css/site.css';
22
import 'bootstrap';
33
import * as React from 'react';
44
import * as ReactDOM from 'react-dom';
5-
import { browserHistory, Router } from 'react-router';
65
import { Provider } from 'react-redux';
7-
import { syncHistoryWithStore } from 'react-router-redux';
6+
import { ConnectedRouter } from 'react-router-redux';
7+
import { createBrowserHistory } from 'history';
88
import routes from './routes';
99
import configureStore from './configureStore';
1010
import { ApplicationState } from './store';
1111

12+
// Create browser history to use in the Redux store
13+
const history = createBrowserHistory();
14+
1215
// Get the application-wide store instance, prepopulating with state from the server where available.
1316
const initialState = (window as any).initialReduxState as ApplicationState;
14-
const store = configureStore(initialState);
15-
const history = syncHistoryWithStore(browserHistory, store);
17+
const store = configureStore(history, initialState);
1618

1719
// This code starts up the React app when it runs in a browser. It sets up the routing configuration
1820
// and injects the app into a DOM element.
1921
ReactDOM.render(
2022
<Provider store={ store }>
21-
<Router history={ history } children={ routes } />
23+
<ConnectedRouter history={ history } children={ routes } />
2224
</Provider>,
2325
document.getElementById('react-app')
2426
);
Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,46 @@
11
import * as React from 'react';
22
import { Provider } from 'react-redux';
33
import { renderToString } from 'react-dom/server';
4-
import { match, RouterContext } from 'react-router';
5-
import createMemoryHistory from 'history/lib/createMemoryHistory';
4+
import { StaticRouter } from 'react-router-dom';
5+
import { replace } from "react-router-redux";
6+
import { createMemoryHistory } from 'history';
67
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
78
import routes from './routes';
89
import configureStore from './configureStore';
910

1011
export default createServerRenderer(params => {
1112
return new Promise<RenderResult>((resolve, reject) => {
12-
// Match the incoming request against the list of client-side routes
13-
const store = configureStore();
14-
match({ routes, location: params.location }, (error, redirectLocation, renderProps: any) => {
15-
if (error) {
16-
throw error;
17-
}
13+
// Create memory history to use in the Redux store
14+
const history = createMemoryHistory();
15+
const store = configureStore(history);
1816

19-
// If there's a redirection, just send this information back to the host application
20-
if (redirectLocation) {
21-
resolve({ redirectUrl: redirectLocation.pathname });
22-
return;
23-
}
17+
// Dispatch the current location so that the router knows where to go
18+
store.dispatch(replace(params.location));
2419

25-
// If it didn't match any route, renderProps will be undefined
26-
if (!renderProps) {
27-
throw new Error(`The location '${ params.url }' doesn't match any route configured in react-router.`);
28-
}
20+
const context : any = {};
2921

30-
// Build an instance of the application
31-
const app = (
32-
<Provider store={ store }>
33-
<RouterContext {...renderProps} />
34-
</Provider>
35-
);
22+
const app = (
23+
<Provider store={ store }>
24+
<StaticRouter context={ context } location={ params.location.path } children={ routes } />
25+
</Provider>
26+
);
3627

37-
// Perform an initial render that will cause any async tasks (e.g., data access) to begin
38-
renderToString(app);
28+
// Perform an initial render that will cause any async tasks (e.g., data access) to begin
29+
renderToString(app);
3930

40-
// Once the tasks are done, we can perform the final render
41-
// We also send the redux store state, so the client can continue execution where the server left off
42-
params.domainTasks.then(() => {
43-
resolve({
44-
html: renderToString(app),
45-
globals: { initialReduxState: store.getState() }
46-
});
47-
}, reject); // Also propagate any errors back into the host application
48-
});
31+
// If there's a redirection, just send this information back to the host application (Maybe improve this?)
32+
if (context.url) {
33+
resolve({ redirectUrl: context.url });
34+
return;
35+
}
36+
37+
// Once the tasks are done, we can perform the final render
38+
// We also send the redux store state, so the client can continue execution where the server left off
39+
params.domainTasks.then(() => {
40+
resolve({
41+
html: renderToString(app),
42+
globals: { initialReduxState: store.getState() }
43+
});
44+
}, reject); // Also propagate any errors back into the host application
4945
});
5046
});

templates/ReactReduxSpa/ClientApp/components/Counter.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { Link } from 'react-router';
2+
import { Link, RouteComponentProps } from 'react-router-dom';
33
import { connect } from 'react-redux';
44
import { ApplicationState } from '../store';
55
import * as CounterStore from '../store/Counter';
@@ -22,7 +22,7 @@ class Counter extends React.Component<CounterProps, {}> {
2222
}
2323

2424
// Wire up the React component to the Redux store
25-
export default connect(
25+
export default connect<CounterStore.CounterState, {}, RouteComponentProps<{}>>(
2626
(state: ApplicationState) => state.counter, // Selects which state properties are merged into the component's props
2727
CounterStore.actionCreators // Selects which action creators are merged into the component's props
2828
)(Counter);

templates/ReactReduxSpa/ClientApp/components/FetchData.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
import * as React from 'react';
2-
import { Link } from 'react-router';
2+
import { Link, RouteComponentProps } from 'react-router-dom';
33
import { connect } from 'react-redux';
44
import { ApplicationState } from '../store';
55
import * as WeatherForecastsState from '../store/WeatherForecasts';
66

77
// At runtime, Redux will merge together...
88
type WeatherForecastProps =
9-
WeatherForecastsState.WeatherForecastsState // ... state we've requested from the Redux store
10-
& typeof WeatherForecastsState.actionCreators // ... plus action creators we've requested
11-
& { params: { startDateIndex: string } }; // ... plus incoming routing parameters
9+
WeatherForecastsState.WeatherForecastsState // ... state we've requested from the Redux store
10+
& typeof WeatherForecastsState.actionCreators // ... plus action creators we've requested
11+
& RouteComponentProps<{ startDateIndex: string }>; // ... plus incoming routing parameters
1212

1313
class FetchData extends React.Component<WeatherForecastProps, {}> {
1414
componentWillMount() {
1515
// This method runs when the component is first added to the page
16-
let startDateIndex = parseInt(this.props.params.startDateIndex) || 0;
16+
let startDateIndex = parseInt(this.props.match.params.startDateIndex) || 0;
1717
this.props.requestWeatherForecasts(startDateIndex);
1818
}
1919

2020
componentWillReceiveProps(nextProps: WeatherForecastProps) {
2121
// This method runs when incoming props (e.g., route params) change
22-
let startDateIndex = parseInt(nextProps.params.startDateIndex) || 0;
22+
let startDateIndex = parseInt(nextProps.match.params.startDateIndex) || 0;
2323
this.props.requestWeatherForecasts(startDateIndex);
2424
}
2525

@@ -67,7 +67,7 @@ class FetchData extends React.Component<WeatherForecastProps, {}> {
6767
}
6868
}
6969

70-
export default connect(
70+
export default connect<WeatherForecastsState.WeatherForecastsState, {}, WeatherForecastProps>(
7171
(state: ApplicationState) => state.weatherForecasts, // Selects which state properties are merged into the component's props
7272
WeatherForecastsState.actionCreators // Selects which action creators are merged into the component's props
7373
)(FetchData);

templates/ReactReduxSpa/ClientApp/components/Layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import { NavMenu } from './NavMenu';
33

44
export interface LayoutProps {
5-
body: React.ReactElement<any>;
5+
children?: React.ReactElement<any>;
66
}
77

88
export class Layout extends React.Component<LayoutProps, {}> {
@@ -13,7 +13,7 @@ export class Layout extends React.Component<LayoutProps, {}> {
1313
<NavMenu />
1414
</div>
1515
<div className='col-sm-9'>
16-
{ this.props.body }
16+
{ this.props.children }
1717
</div>
1818
</div>
1919
</div>;

templates/ReactReduxSpa/ClientApp/components/NavMenu.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { Link } from 'react-router';
2+
import { NavLink, Link } from 'react-router-dom';
33

44
export class NavMenu extends React.Component<{}, {}> {
55
public render() {
@@ -18,19 +18,19 @@ export class NavMenu extends React.Component<{}, {}> {
1818
<div className='navbar-collapse collapse'>
1919
<ul className='nav navbar-nav'>
2020
<li>
21-
<Link to={ '/' } activeClassName='active'>
21+
<NavLink exact to={ '/' } activeClassName='active'>
2222
<span className='glyphicon glyphicon-home'></span> Home
23-
</Link>
23+
</NavLink>
2424
</li>
2525
<li>
26-
<Link to={ '/counter' } activeClassName='active'>
26+
<NavLink to={ '/counter' } activeClassName='active'>
2727
<span className='glyphicon glyphicon-education'></span> Counter
28-
</Link>
28+
</NavLink>
2929
</li>
3030
<li>
31-
<Link to={ '/fetchdata' } activeClassName='active'>
31+
<NavLink to={ '/fetchdata' } activeClassName='active'>
3232
<span className='glyphicon glyphicon-th-list'></span> Fetch data
33-
</Link>
33+
</NavLink>
3434
</li>
3535
</ul>
3636
</div>

templates/ReactReduxSpa/ClientApp/configureStore.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import { createStore, applyMiddleware, compose, combineReducers, GenericStoreEnhancer } from 'redux';
22
import thunk from 'redux-thunk';
3-
import { routerReducer } from 'react-router-redux';
3+
import { routerReducer, routerMiddleware } from 'react-router-redux';
44
import * as Store from './store';
5+
import { History } from 'history';
56

6-
export default function configureStore(initialState?: Store.ApplicationState) {
7+
export default function configureStore(history: History, initialState?: Store.ApplicationState) {
78
// Build middleware. These are functions that can process the actions before they reach the store.
89
const windowIfDefined = typeof window === 'undefined' ? null : window as any;
910
// If devTools is installed, connect to it
1011
const devToolsExtension = windowIfDefined && windowIfDefined.devToolsExtension as () => GenericStoreEnhancer;
12+
const middlewares = [thunk, routerMiddleware(history)];
1113
const createStoreWithMiddleware = compose(
12-
applyMiddleware(thunk),
14+
applyMiddleware(...middlewares),
1315
devToolsExtension ? devToolsExtension() : f => f
1416
)(createStore);
1517

templates/ReactReduxSpa/ClientApp/routes.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import * as React from 'react';
2-
import { Router, Route, HistoryBase } from 'react-router';
2+
import { Route } from 'react-router-dom';
33
import { Layout } from './components/Layout';
44
import Home from './components/Home';
55
import FetchData from './components/FetchData';
66
import Counter from './components/Counter';
77

8-
export default <Route component={ Layout }>
9-
<Route path='/' components={{ body: Home }} />
10-
<Route path='/counter' components={{ body: Counter }} />
11-
<Route path='/fetchdata' components={{ body: FetchData }}>
12-
<Route path='(:startDateIndex)' /> { /* Optional route segment that does not affect NavMenu highlighting */ }
13-
</Route>
14-
</Route>;
8+
export default <Layout>
9+
<Route exact path='/' component={ Home } />
10+
<Route path='/counter' component={ Counter } />
11+
<Route path='/fetchdata/:startDateIndex?' component={ FetchData } />
12+
</Layout>;
1513

1614
// Enable Hot Module Replacement (HMR)
1715
if (module.hot) {

templates/ReactReduxSpa/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"@types/react": "^0.14.29",
77
"@types/react-dom": "^0.14.14",
88
"@types/react-redux": "^4.4.29",
9-
"@types/react-router": "^4.0.4",
9+
"@types/react-router-dom": "^4.0.3",
1010
"@types/react-router-redux": "^5.0.0",
1111
"@types/redux": "3.5.27",
1212
"@types/webpack": "^2.2.0",

0 commit comments

Comments
 (0)
0