Skip to content

Commit

Permalink
hot reload + api / production on 3000 to match CRA
Browse files Browse the repository at this point in the history
  • Loading branch information
kdmcclel committed May 1, 2017
1 parent 75b05cc commit c35b9e4
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 45 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ Install
-------
```bash
npm install
npm run build
npm run start:server
```

### Development
This just runs create react app, its great!
This runs the create react app with hot reloading containers + reducers and the api backend
```bash
npm start
```

### Production
-------
```bash
npm run build
npm run now-start
```

10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
"name": "ssr-create-react-app-v2",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:3001/",
"devDependencies": {
"babel-cli": "^6.24.1",
"concurrently": "^3.4.0",
"react-scripts": "0.8.5"
},
"dependencies": {
Expand All @@ -16,12 +18,14 @@
"react-dom": "^15.5.4",
"react-redux": "^5.0.4",
"react-router-dom": "^4.1.1",
"redux": "^3.6.0"
"redux": "^3.6.0",
"redux-saga": "^0.15.0"
},
"scripts": {
"start": "react-scripts start",
"start": "./node_modules/.bin/concurrently \"npm run start:server\" \"npm run start:client\"",
"start:client": "react-scripts start",
"start:server": "NODE_ENV=development node server/index.js",
"now-start": "NODE_ENV=production node server/index.js",
"now-start": "NODE_ENV=production PORT=3000 node server/index.js",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
Expand Down
11 changes: 10 additions & 1 deletion server/routes/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,14 @@ router.get('/', function(req, res, next) {
res.json({})
})

const users = {
1: { email: '[email protected]' }
}
router.get('/users/:id', function(req, res, next) {
if(Object.keys(users).indexOf(req.params.id) > -1) {
res.json(users[req.params.id])
} else {
res.status(404).json({})
}
})
module.exports = router

10 changes: 8 additions & 2 deletions src/actions/user.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { SET, RESET } from '../types/user'
import { REQUEST, SET, RESET } from '../types/user'

export function request(id) {
return {
type: REQUEST,
id
}
}

export function set(payload){
return {
Expand All @@ -12,4 +19,3 @@ export function reset(){
type: RESET
}
}

51 changes: 46 additions & 5 deletions src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ class Api {
if (!options){
return
}
const {token} = options
const {token, prefix} = options
this.token = token
this.prefix = prefix
}
getJsonHeaders(){
return {
Expand All @@ -34,11 +35,51 @@ class Api {
_buildQueryString(data){
return '?' + Object.keys(data).map(d=>d+'='+encodeURIComponent(data[d]))
}
parseJson(res) {
return res.json()
}
checkStatus(res) {
if (res.status >= 200 && res.status < 300) {
return res
} else {
var error = new Error(res.statusText)
error.response = res
throw error
}
}
get(fixture, id) {
if(id) {
return fetch(`${this.apiUrl}${this.prefix}/${fixture}/${id}`, this.getJsonHeaders())
.then(this.checkStatus)
.then(this.parseJson)
.then(data => {
return {ok: true, data}
}).catch(err => {
return {ok: false, err}
})
} else {
return fetch(`${this.apiUrl}${this.prefix}/${fixture}`, this.getJsonHeaders())
.then(this.checkStatus)
.then(this.parseJson)
.then(data => {
return {ok: true, data}
}).catch(err => {
return {ok: false, err}
})
}
}
}

export class MainApi extends Api{
constructor(options){
super(options)
this.prefix = '/api'
const create = (prefix = '/api') => {
const api = new Api({prefix})

const getUser = (id) => api.get('users', id)

return {
getUser
}
}

export default {
create
}
2 changes: 1 addition & 1 deletion src/config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
let apiUrl = 'http://localhost:3001'
let apiUrl = 'http://localhost:3000'
if (process.env.NODE_ENV === 'production') {
apiUrl = ''
}
Expand Down
3 changes: 3 additions & 0 deletions src/containers/SecondPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { Link } from 'react-router-dom'
import './SecondPage.css'

class SecondPage extends Component {
componentDidMount() {
this.props.userActions.request(1)
}
render() {
return (
<div className='bold'>
Expand Down
25 changes: 17 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@ import App from './containers/App'
const initialState = {}
const store = configureStore(initialState)

ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root')
)
const render = (Component) => {
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<Component />
</BrowserRouter>
</Provider>,
document.getElementById('root')
)
}

render(App)

if(process.env.NODE_ENV === 'development' && module.hot) {
module.hot.accept('./containers/App', () => {
const NewApp = require('./containers/App').default
render(NewApp)
})
}
11 changes: 11 additions & 0 deletions src/sagas/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { fork } from 'redux-saga/effects'
import userSaga from './user'
import API from '../api'

const api = API.create()

export default function * rootSaga() {
yield [
fork(userSaga(api))
]
}
27 changes: 27 additions & 0 deletions src/sagas/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as userActions from '../actions/user'
import * as userTypes from '../types/user'
import { call, put, takeLatest } from 'redux-saga/effects'

function * getUser(api, {id}) {
if(!id) {
yield put(userActions.reset())
} else {
const response = yield call(api.getUser, id)

if(response.ok) {
yield put(userActions.set(response.data))
} else {
yield put(userActions.reset())
}
}
}

const userSaga = (api) => {
return function * () {
yield [
takeLatest(userTypes.REQUEST, getUser, api)
]
}
}

export default userSaga
15 changes: 11 additions & 4 deletions src/store.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { createStore, applyMiddleware, compose } from 'redux'
import reducers from './reducers'
import rootSaga from './sagas'
//import createLogger from 'redux-logger'
//import createSagaMiddleware from 'redux-saga'
import createSagaMiddleware from 'redux-saga'

//const logger = createLogger()
//const sagaMiddleware = createSagaMiddleware()
const sagaMiddleware = createSagaMiddleware()

export default function configureStore(initialState = {}) {
// Create the store with two middlewares
const middlewares = [
// sagaMiddleware
sagaMiddleware
//, logger
]

Expand All @@ -24,8 +25,14 @@ export default function configureStore(initialState = {}) {
)

// Extensions
//store.runSaga = sagaMiddleware.run
sagaMiddleware.run(rootSaga)
store.asyncReducers = {} // Async reducer registry

if(process.env.NODE_ENV === 'development' && module.hot) {
module.hot.accept('./reducers', () => {
store.replaceReducer(require('./reducers').default)
})
}

return store
}
2 changes: 1 addition & 1 deletion src/types/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const prefix = 'USER/'

export const REQUEST = prefix + 'REQUEST'
export const SET = prefix + 'SET'
export const RESET = prefix + 'RESET'

Loading

0 comments on commit c35b9e4

Please sign in to comment.