Skip to content

Commit

Permalink
switch to react-router v1beta2
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Marcello committed Sep 24, 2015
1 parent babd2ef commit 8a81346
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 65 deletions.
35 changes: 35 additions & 0 deletions app/client.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

import "babel/polyfill";
import React from "react";
import BrowserHistory from "react-router/lib/BrowserHistory";
import Location from "react-router/lib/Location";
import queryString from "query-string";
import createStore from "./redux/store";
import ApiClient from "./helpers/ApiClient";
import universalRouter from "./helpers/universalRouter";

const history = new BrowserHistory();
const client = new ApiClient();

const dest = document.getElementById('content');
const store = createStore(client, window.__data);
const search = document.location.search;
const query = search && queryString.parse(search); // TODO: research the specifics of this
const location = new Location(document.location.pathname, query);

universalRouter(location, history, store)
.then(({ component }) => {
React.render(component, dest);
if (__DEVTOOLS__) {
console.log("devtois");
const { DevTools, DebugPanel, LogMonitor } = require('redux-devtools/lib/react');
React.render(<div>
{ component }
<DebugPanel top right bottom key="debugPanel">
<DevTools store={ store } monitor={ LogMonitor } />
</DebugPanel>
</div>, dest);

}
})

7 changes: 2 additions & 5 deletions app/containers/Application.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import React from "react";
import { Link } from "react-router";

import styles from "./Application.css";

//TODO: this code sux
import { renderDevTools } from "../../config/mainRenderer";

export default class Application extends React.Component {
render() {
return <div className={ styles.application }>
<h1>This is an applidjfcation container</h1>
<h1>This is an application container</h1>
{ this.props.children }
{ renderDevTools() }
</div>
}
}
Expand Down
37 changes: 37 additions & 0 deletions app/helpers/ApiClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import superagent from 'superagent';

class ApiClient_ {
constructor(req) {
['get', 'post', 'put', 'patch', 'del'].
forEach((method) => {
this[method] = (path, options) => {
return new Promise((resolve, reject) => {
const request = superagent[method](this.formatUrl(path));
if (options && options.params) {
request.query(options.params);
}
if (options && options.data) {
request.send(options.data);
}
request.end((err, res) => {
if (err) {
reject((res && res.body) || err);
} else {
resolve(res.body);
}
});
});
};
});
}

/* This was originally a standalone function outside of this class, but babel kept breaking, and this fixes it */
formatUrl(path) {
const adjustedPath = path[0] !== '/' ? '/' + path : path;
//TODO this will need to be adjusted
return '/api' + adjustedPath;
}
}
const ApiClient = ApiClient_;

export default ApiClient;
61 changes: 61 additions & 0 deletions app/helpers/universalRouter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import Router from 'react-router';
import createRoutes from '../routes';
import { Provider } from 'react-redux';

const getFetchData = (component = {}) => {
//TODO: research this WrappedComponent attribue
return component.WrappedComponent ?
getFetchData(component.WrappedComponent) :
component.fetchData;
};

export function createTransitionHook(store) {
return (nextState, transition, callback) => {
const { params, location: { query } } = nextState;
const promises = nextState.branch
.map(route => route.component) // pull out individual route components
.filter((component) => getFetchData(component)) // only look at ones with a static fetchData()
.map(getFetchData) // pull out fetch data methods
.map(fetchData => fetchData(store, params, query || {})); // call fetch data methods and save promises
Promise.all(promises)
.then(() => {
callback(); // can't just pass callback to then() because callback assumes first param is error
}, (error) => {
callback(error);
});
};
}

export default function universalRouter(location, history, store) {
const routes = createRoutes(store);
return new Promise((resolve, reject) => {
Router.run(routes, location, [createTransitionHook(store)], (error, initialState, transition) => {
if (error) {
return reject(error);
}

if (transition && transition.redirectInfo) {
return resolve({
transition,
isRedirect: true
});
}

if (history) { // only on client side
initialState.history = history;
}

const component = (
<Provider store={store} key="provider">
{() => <Router {...initialState} children={routes}/>}
</Provider>
);

return resolve({
component,
isRedirect: false
});
});
});
}
25 changes: 25 additions & 0 deletions app/redux/middleware/clientMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default function clientMiddleware(client) {
return ({dispatch, getState}) => {
return next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}

const { promise, types, ...rest } = action;
if (!promise) {
return next(action);
}

const [REQUEST, SUCCESS, FAILURE] = types;
next({...rest, type: REQUEST});
return promise(client).then(
(result) => next({...rest, result, type: SUCCESS}),
(error) => next({...rest, error, type: FAILURE})
).catch((error)=> {
console.error('MIDDLEWARE ERROR:', error);
next({...rest, error, type: FAILURE});
});
};
};
}

38 changes: 17 additions & 21 deletions app/redux/store.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
import { createStore, applyMiddleware, compose } from 'redux';

//TODO: implement middleware for async actions
// import createMiddleware from './middleware/clientMiddleware';
import createMiddleware from './middleware/clientMiddleware';

export default function createApiClientStore(client, data) {
// const middleware = createMiddleware(client);
const middleware = createMiddleware(client);
let finalCreateStore;
if ( __DEVTOOLS__) {
const { devTools, persistState } = require('redux-devtools');
finalCreateStore = compose(
// applyMiddleware(middleware),
devTools(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(createStore);
}
else {
// finalCreateStore = applyMiddleware(middleware)(createStore);
finalCreateStore = creteStore
if (__DEVTOOLS__) {
const { devTools, persistState } = require('redux-devtools');
finalCreateStore = compose(
applyMiddleware(middleware),
devTools(),
persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
)(createStore);
} else {
finalCreateStore = applyMiddleware(middleware)(createStore);
}

const reducer = require('./modules/reducer');
const store = finalCreateStore(reducer, data);
store.client = client;


//TODO: set up hotmodule replacement
if (__DEVELOPMENT__ && module.hot) {
module.hot.accept('./modules/reducer', () => {
store.replaceReducer(require('./modules/reducer'));
});
}
module.hot.accept('./modules/reducer', () => {
store.replaceReducer(require('./modules/reducer'));
});
}

return store;
}

3 changes: 3 additions & 0 deletions app/route-handlers/Application.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import Application from "containers/Application";


//TODO: revist the necessity of this route-handlers abstraction

//TODO: eventually we will want to connect to store using react-redux;
// export default connect(mapStateToProps)(Application)

Expand Down
15 changes: 7 additions & 8 deletions app/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import { Route } from "react-router";
import Application from "route-handlers/Application";
import Login from "route-handlers/Login";

//TODO: research necessity of Object.assign polyfill
export default function(store) {
return (
<Route name="app" path="/" component={ Application }>
<Route name="login" path="login" component={ Login }/>
</Route>
)
}

//TODO: switch to es6 export

module.exports = (
<Route name="app" path="/" component={ Application }>
<Route name="login" path="login" component={ Login }/>
</Route>
)
28 changes: 0 additions & 28 deletions config/mainRenderer.jsx

This file was deleted.

4 changes: 2 additions & 2 deletions make-webpack-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var loadersByExtension = require("./config/loadersByExtension");

module.exports = function(options) {
var entry = {
main: "./config/mainRenderer"
main: "./app/client"
};
var loaders = {
"jsx": options.hotComponents ? ["react-hot-loader", "babel-loader?stage=0"] : "babel-loader?stage=0",
Expand Down Expand Up @@ -69,7 +69,7 @@ module.exports = function(options) {
if (options.debug) {
plugins.push(new webpack.DefinePlugin({
__DEVTOOLS__ : true,
__DEVELOPMENT__: true
__DEVELOPMENT__: true,
}));
}

Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
"author": "Matt Marcelo",
"license": "ISC",
"dependencies": {
"babel": "^5.8.23",
"babel-core": "^5.8.25",
"babel-loader": "^5.3.2",
"css-loader": "^0.19.0",
"extract-text-webpack-plugin": "^0.8.2",
"file-loader": "^0.8.4",
"history": "^1.10.1",
"html-loader": "^0.3.0",
"json-loader": "^0.5.3",
"json5": "^0.4.0",
Expand All @@ -25,15 +27,17 @@
"less-loader": "^2.2.1",
"markdown-loader": "^0.1.7",
"node-sass": "^3.3.3",
"query-string": "^2.4.1",
"raw-loader": "^0.5.1",
"react": "^0.13.3",
"react-hot-loader": "^1.3.0",
"react-router": "^1.0.0-rc1",
"react-redux": "^3.0.0",
"redux": "^3.0.0",
"sass-loader": "^2.0.1",
"stats-webpack-plugin": "^0.2.1",
"style-loader": "^0.12.4",
"stylus-loader": "^1.3.0",
"superagent": "^1.4.0",
"url-loader": "^0.5.6",
"webpack": "^1.12.2"
},
Expand Down

0 comments on commit 8a81346

Please sign in to comment.