-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathREADME.html
More file actions
410 lines (366 loc) · 15.2 KB
/
README.html
File metadata and controls
410 lines (366 loc) · 15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
<h1>Fullstack Junior-Phase Boilerplate</h1>
<ul>
<li><a href="#fullstack-junior-phase-boilerplate">Fullstack Junior-Phase Boilerplate</a>
<ul>
<li><a href="#overview">OVERVIEW</a></li>
<li><a href="#express">Express</a></li>
<li><a href="#postgresql">PostgreSQL</a></li>
<li><a href="#sequelize">Sequelize</a></li>
<li><a href="#socketio">Socket.IO</a></li>
<li><a href="#react">React</a></li>
<li><a href="#redux">Redux</a></li>
<li><a href="#react-redux">React-Redux</a></li>
<li><a href="#webpack">Webpack</a></li>
<li><a href="#thunk">Thunk</a></li>
</ul>
</li>
</ul>
<h2>OVERVIEW</h2>
<h3>WHAT THIS IS</h3>
<p>[an attempt at] A concise, easy-to-read reference to get your projects up and running as quickly as possible. All the "setup" code you need will be found here.</p>
<h3>WHAT THIS ISN'T</h3>
<p>This is NOT meant to be a “copy-paste and go” sort of reference, but rather a quick “reminder” sheet. You will still have to know what this code does and adapt it to your own needs.</p>
<h3>HOW TO EDIT</h3>
<p><a href="https://github.com/mjthor86/fullstack-boilerplate#fork-destination-box">Fork this repo</a> and <a href="https://help.github.com/articles/creating-a-pull-request/">make a pull request</a>! This document was written in <a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet">Markdown</a>, and the Gulp task to build the HTML is included <a href="https://code.visualstudio.com/docs/languages/markdown">(VSC instructions here)</a>.</p>
<p>It isn't <em>required</em> to build the HTML for your pull requests, but please at least write them in Markdown!</p>
<h2>Express</h2>
<h3>NPM Modules</h3>
<pre><code>npm install --save express body-parser {morgan OR volleyball}
</code></pre>
<h3>Dependencies</h3>
<pre><code class="language-javascript">const express = require('express');
const path = require('path'); // path formatting utility
const bodyParser = require('body-parser'); // parsing middleware
const morgan = require('morgan'); // logging middleware, can substitute with volleyball
</code></pre>
<h3>Setup</h3>
<pre><code class="language-javascript">// define express server
const app = express();
// use morgan logging middleware
app.use(morgan('dev'));
// use body-parser middleware
app.use(bodyParser.json()); // parse JSON requests
app.use(bodyParser.urlencoded({ extended: true })); // parse URL requests
// static routing for /public/ path
app.use(express.static(path.join(__dirname, '..', 'public')));
// send index.html
app.use('*', (req, res, next) =>
res.sendFile(path.join(__dirname, '..', 'public/index.html'))
);
// start server and listen on port 3000 (usually done after a db.sync)
app.listen(3000, () => console.log(`server listening on port 3000`));
// error-handling, should come AFTER all other routes
app.use((err, req, res, next) =>
res.status(err.status || 500).send(err.message || 'Internal server error.')
);
</code></pre>
<h3>Express Router</h3>
<pre><code class="language-javascript">// in 'index.js' or 'start' file
const apiRouter = require('./api'); // will depend on route and file structure
app.use('/api', apiRouter);
// in '/api/index.js'
const router = require('express').Router();
module.exports = router;
// write routes (i.e. router.get(), router.set() or sub-routes)
</code></pre>
<h2>PostgreSQL</h2>
<p>This one is pretty simple: you just need to run ‘createdb {server_name}’ on the command line. Most of the work we do with Postgres is via Sequelize.</p>
<p>If anyone else can think of something we need to remember here for setup, please suggest it!</p>
<h2>Sequelize</h2>
<h3>NPM Packages</h3>
<pre><code>npm install --save sequelize pg {pg-native AND/OR pg-hstore}
</code></pre>
<h3>Dependencies</h3>
<pre><code class="language-javascript">const Sequelize = require('sequelize');
</code></pre>
<h3>Setup</h3>
<pre><code class="language-javascript">const db = new Sequelize('postgres://localhost:5432/DB-NAME-HERE', {
logging: false,
native: true // omit this line if using pg-hstore
}
);
</code></pre>
<h2>Socket.IO</h2>
<h3>NPM Packages</h3>
<pre><code>npm install socket.io --save
</code></pre>
<h3>Create Server</h3>
<pre><code class="language-javascript">const socketio = require('socket.io');
// This part below app.listen so the express app has priority
const io = socketio(server);
</code></pre>
<h3>User Socket Server as Event-Emitter</h3>
<pre><code class="language-javascript">io.on('connection', function (socket) {
/* This function receives the newly connected socket.
This function will be called for EACH browser that connects to our server.
i.e. If Ben and Matt both connect to the server, this will run once when Ben
connects, and once when Matt connects */
console.log('A new client has connected!');
console.log(socket.id);
});
</code></pre>
<h3>Creating Socket Event</h3>
<pre><code class="language-javascript">// Never seen window.location before?
// This object describes the URL of the page we're on!
var socket = io(window.location.origin);
socket.on('connect', function () {
console.log('I have made a persistent two-way connection to the server!');
});
// **Remember: socket refers to one individual socket
// io refers to every socket
</code></pre>
<h3>Quick reference for methods below:</h3>
<pre><code class="language-javascript">socket.emit('message', "this is a test"); //sending to sender-client only
socket.broadcast.emit('message', "this is a test"); //sending to all clients except sender
socket.broadcast.to('game').emit('message', 'nice game'); //sending to all clients in 'game' room(channel) except sender
socket.to('game').emit('message', 'enjoy the game'); //sending to sender client, only if they are in 'game' room(channel)
socket.broadcast.to(socketid).emit('message', 'for your eyes only'); //sending to individual socketid
io.emit('message', "this is a test"); //sending to all clients, include sender
io.in('game').emit('message', 'cool game'); //sending to all clients in 'game' room(channel), include sender
io.of('myNamespace').emit('message', 'gg'); //sending to all clients in namespace 'myNamespace', include sender
socket.emit(); //send to all connected clients
socket.broadcast.emit(); //send to all connected clients except the one that sent the message
socket.on(); //event listener, can be called on client to execute on server
io.sockets.socket(); //for emitting to specific clients
io.sockets.emit(); //send to all connected clients (same as socket.emit)
io.sockets.on() ; //initial connection from a client.
</code></pre>
<h2>React</h2>
<h3>Tom’s Super Important Laws</h3>
<p>State must ALWAYS be initialized with the appropriate data type.
Dumb components should be as dumb as possible, they should only calculate the view and nothing more.
All asynchronous behavior (such as AJAX) and side effects should go into a thunk.</p>
<h3>NPM Packages</h3>
<pre><code>npm install --save react react-router-dom
</code></pre>
<h3>Dependencies</h3>
<pre><code class="language-javascript">import React from 'react';
import { HashRouter as Router, Route, Link } from 'react-router-dom';
</code></pre>
<h3>Creating a Smart Component and Using React Router</h3>
<pre><code class="language-javascript">export default class ViewPets extends React.Component {
constructor(props) {
super(props);
this.state = {
view: 'all',
property: 'value'
};
}
changeState(view) {
this.setState({
view: view,
property: 'newValue'
})
}
componentDidMount() {
this.setState({ view: this.props.match.params.view })
}
render() {
let animals = catsData.concat(dogsData);
if(this.state.view === 'cats' {
animals = catsData;
}
return (
<div>
<h1>Pets</h1>
<AnimalList animals={animals} />
</div>
)
}
}
</code></pre>
<h3>Creating a Dumb Component</h3>
<pre><code class="language-javascript">const AnimalList = ({ animals }) => {
return (
<div className="gallery">
{ animals.map(animal => {
return <AnimalCard key={animal.id} animal={animal} />;
})}
</div>
)
}
</code></pre>
<h3>Component Lifecycle Methods</h3>
<pre><code class="language-javascript">/* Mounting */
constructor(props) {} // Called before a component is mounted.
componentWillMount() {} // Called immediately before mounting occurs.
render() {} // Renders using returned JSX
componentDidMount() {} // Called immediately after mounting.
/* Updating */
componentWillReceiveProps(nextProps) {} // Invoked before a mounted component gets new props.
shouldComponentUpdate(nextProps, nextState) {} // Invoked before rendering when new props or state are received.
componentWillUpdate(nextProps, nextState) {} // Invoked immediately before rendering after new props are received.
componentDidUpdate(prevProps, prevState) {} // Invoked immediately after updating occurs, but not called on initial render.
/* Unmounting */
componentWillUnmount() {} // Invoked immediately before a component is unmounted or destroyed.
/* Error Handling */
componentDidCatch(error, info) {} // Catches errors anywhere in child component tree.
</code></pre>
<h3>Using React Router</h3>
<pre><code class="language-javascript">// Inside a Component
render() {
return (
<Router>
<div className="col-xs-10">
<Route exact path="/" component={AllAlbums} />
<Route path="/albums" component={AllAlbums} />
</div>
</Router>
);
}
</code></pre>
<h3>Moar Code</h3>
<pre><code class="language-javascript">
</code></pre>
<h2>Redux</h2>
<h3>NPM Packages</h3>
<pre><code>npm install --save ***
</code></pre>
<h3>Dependencies</h3>
<pre><code class="language-javascript">// In store.js
import { createStore } from 'redux';
</code></pre>
<h3>Define the Initial State</h3>
<pre><code class="language-javascript">const initialState = {
counter: 0,
};
</code></pre>
<h3>Define Action Types</h3>
<pre><code class="language-javascript">const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
</code></pre>
<h3>Define Action Creator</h3>
<pre><code class="language-javascript">export function incrementCounter(interval) {
return {
type: INCREMENT_COUNTER,
interval
}
}
</code></pre>
<h3>Define Reducer</h3>
<pre><code class="language-javascript">function reducer(prevState=initialState, action) {
switch(action.type) {
case INCREMENT_COUNTER:
let newState = Object.assign({}, prevState);
newState.counter += action.interval;
return newState;
default:
return prevState;
}
};
</code></pre>
<h3>Define and Export Store</h3>
<pre><code class="language-javascript">const store = createStore(reducer);
export default store;
</code></pre>
<h2>React-Redux</h2>
<h3>NPM Packages</h3>
<pre><code>npm install --save react-redux
</code></pre>
<h3>Dependencies (index.js)</h3>
<pre><code class="language-javascript">import { Provider } from 'react-redux';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom'; // choose router type
import { Main } from './components'; // will depend on where your Main.js is defined
import store from './store'; // will depend on where your store is defined
</code></pre>
<h3>Setup (index.js)</h3>
<pre><code class="language-javascript">ReactDOM.render(
<Provider store={store}>
<Router>
<Main /> // render your main component
</Router>
</Provider>,
document.getElementById('app') // second argument to render(), references root node in your HTML
);
</code></pre>
<h3>Dependencies (connected component)</h3>
<pre><code class="language-javascript">import { connect } from 'react-redux';
</code></pre>
<h3>Setup (connected component)</h3>
<pre><code class="language-javascript">/* code here */
</code></pre>
<h2>Webpack</h2>
<h3>Webpack.config.js</h3>
<pre><code class="language-javascript">'use strict';
// The exports is a configuration object that tells webpack what to do
module.exports = {
// The entry field tells webpack where our application starts.
// Webpack will start building this file and any subsequent file(s) that are imported by that file
entry: './browser/react/index.js',
// The output field specifies where webpack's output will go. In this case, we've specified
// that it should put it into a file called bundle.js in our public directory
output: {
path: __dirname,
filename: './public/bundle.js'
},
// The context field simply sets the context for relative pathnames
context: __dirname,
// This handy option tells webpack to create another, special file called "bundle.js.map".
// This special file is called a "source-map".
// If enabled, your browser will automatically request this file so that it can faithfully re-create your source code in your browser's dev tools.
// This way, when you open the code for debugging in Chrome dev tools, instead of seeing the hard-to-read transpiled code that webpack creates, you'll
// see your clean source code.
// For more info: https://developers.google.com/web/tools/chrome-devtools/javascript/source-maps
devtool: 'source-map',
// Here is where we specify what kinds of special syntax webpack should look out for
module: {
// Loaders are special node modules that we've installed that know how to parse certain syntax.
// There are loaders for all different kinds of syntax.
loaders: [
{
// Here, we want to test and see if any files end with .js or .jsx.
// Only files that match this criteria will be parsed by this loader.
test: /jsx?$/,
// We want webpack to ignore anything in a node_modules or bower_components directory.
// This is very important - modules have a responsibility to build their own js files.
// If we were to do this ourselves, building our bundle.js would take forever!
exclude: /(node_modules|bower_components)/,
// We're using the babel-loader module to read our files - it can handle both ES6 and JSX!
// Babel will use our .babelrc to figure out how to compile our code
loader: 'babel-loader',
// Here, we telling webpack to look for any syntax that looks like ES6 and any syntax that looks like JSX.
// If it finds it, the babel-loader will transpile it for us!
query: {
presets: ['react', 'es2015']
}
}
]
}
};
</code></pre>
<h2>Thunk</h2>
<h3>NPM Packages</h3>
<pre><code>npm install --save redux-thunk
</code></pre>
<h3>Dependencies</h3>
<pre><code>
import axios from 'axios';
import thunkMiddleware from 'redux-thunk';
</code></pre>
<h3>GET Thunk</h3>
<pre><code>export function fetchMessages () {
return function thunk (dispatch) {
return axios.get('/api/messages')
.then(res => res.data)
.then(messages => {
const action = getMessages(messages);
dispatch(action);
});
}
}
</code></pre>
<h3>POST Thunk</h3>
<pre><code>
export function postMessage (message) {
return function thunk (dispatch) {
return axios.post('/api/messages', message)
.then(res => res.data)
.then(newMessage => {
const action = getMessage(newMessage);
dispatch(action);
socket.emit('new-message', newMessage);
});
}
}
</code></pre>