JavaScript
This file does not intend to provide an extensive presentation of all the JavaScript
background needed to start coding the application, but it rather tries to give the minimal information and some external references to avoid confusing the developer who would not be familiar with some features of the language.
ES2015
We use ES2015 syntax thanks to the babel plugin for Webpack. Most of the highlights in this file are related to ES2015 syntax.
Destructuring
Destructuring assignments allow to extract some information from an object or an array. They look like this:
const person = { firstname: 'john', lastname: 'doe' }
const { firstname, lastname } = person
console.log(firstname) // 'john'
console.log(lastname) // 'doe'
This approach can be used for destructuring function arguments. We use them a lot when defining React
components:
function CodeEditor(props) {
let label = props.label
(...)
}
can be rewritten as:
function CodeEditor({ label }) {
return (...)
}
Destructuring assignments are also frequently used in combination with array functions:
var codes = [{ id: '...', label: '...'}, { id: '...', label: '...' }]
codes.map(function (code) {
const id = code.id
const label = code.label
return ... //do something with id and label
}
)
}
can be rewritten as:
var codes = [{ id: '...', label: '...'}, { id: '...', label: '...' }]
codes.map(({id, label}) => ) {
return ... //do something with id and label
}
)
}
Spread operator with arrays
It can be useful to update an array without mutating the initial one:
const initialArray = ['john', 'jack', 'bob']
//keep all except the first one
const [dontCare, ...withoutFirst] = initialArray
//add an entry
const withOneMore = [...initialArray, 'kate']
Spread operator with objects
This syntax is not part of ES2015, but might be part of future JavaScript
versions. Like ES2015 syntax, it is supported thanks to babel, with the transform-object-rest-spread plugin.
It allows to make a copy of some object before updating some properties:
const initialObject = {
id1: 'one',
id2: 'two',
id3: 'three'
}
//`initialObject` won't be modified
const newObject = {
...initialObject,
id2: 'deux'
}
//another way to do this can be
// const newObject = Object.assign(initialObject, { id2: 'deux' })
We can also use the spread operator to keep all the entries except one (useful to remove an entry):
const initialObject = {
john: 25,
jack: 65,
bob: 35
}
//we want to remove the "jack" entry
const {
jack: dontCare,
...withoutJack
} = initialObject
//`withoutJack` is the new object
`
Computed property names
Usually, to create an object when a key comes from a variable, we write that kind of code:
const firstname = 'john'
const names = {}
names[firstname] = 'doe'
In ES2015, with computed property names, we can write:
const firstname = 'john'
const names = {
[firstname]: 'doe'
}
It is particularly useful in combination with the spread operator to enforce immutability when working with objects (like in a reducer). This way, we can make a copy of an object and only update one key:
const initialObject = {
id1: 'one',
id2: 'two',
id3: 'three'
}
const idToProcess = 'id2'
const newValue = 'deux'
const newObject = {
...initialObject,
[idToProcess]: newValue
}
Shorthand property names
ES2015 provides a shorthand to create an object from variables when the object keys match the variables names shorter.
var firstname = 'john'
var lastname = 'doe'
//Before ES2015
var person = {
firstname: firstname,
lastname: lastname
}
//With ES2015
var person = {
firstname,
lastname
}
Arrow functions
Arrow functions are a short and convenient way to write small anonymous functions. They look like this:
const increment = a => a + 1
They are used extensively in this application, but the regular function expression function () {}
is still preferred when the logic is not trivial.
Template literals
Template literals are convenient to describe strings that would be otherwise built by concatenating (+
) multiple strings. For instance in src/js/utils/state-to-model-questionnaire.js:
const makePageBreakGroup = index => ({
name: `PAGE_${index}`,
label: `Components for page ${index}`,
...
})
Another advantage of template literals is that they allow to write multiline strings easily.
Export and import
This application uses export
and import
ES6 statements. Some useful information about them can be found here Error: file not found: /github/workspace/local_source/en/developper-guide/javascript/https:/developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import
and there export.
Briefly, we can keep in mind that there are two kind of export
(s): named exports and default exports. Both are used in this application.
Named exports follow one of these patterns:
export const firstNameExport = ...
//or
export {
secondNamedExport,
thirdNamedExport
}
While default exports look like this:
const mainPurposeOfThisFile = ...
export default mainPurposeOfThisFile
Imports of named exports use curly brackets to identify which exported variables should be imported.
import { firstNameExport, secondNamedExport } from '...'
while default exports are imported with:
import mainPurposeOfThisFile from '...'
When we import a default export, the name we give to the local variable does not need to match the name defined in the module. The code above could be re-written like this:
//We use `aRelevantName` in place of `mainPurposeOfThisFile`
import aRelevantName from '...'
Components
Components live in their own file, so we use export default
to expose them. There might be in the same file a generic definition of the component, what is sometimes called a presentational component, and a 'connected' component. Most of the time, only the 'connected' component will have a purpose for the outside world, so the export looks like this (see src/js/components/component-editor.js):
function ComponentEditor(...) {
}
...
export default connect(mapStateToProps, mapDispatchToProps)(ComponentEditor)
Import css
This application uses style-loader in combination with Webpack. It allows to import css
files from JavaScript. Hence, in src/js/main.js, we import the main css files the application will need:
require('../css/pogues.css')
It is convenient:
- to mark some particular
css
file as a dependency for someJavaScript
code to run properly; - to process the file to make performance optimizations (inline
css
rules in thehead
section of thehtml
file) and compatibility adjustments (for instance by adding prefixes when needed); - to make Webpack taking care of copying this asset to the
dist
folder.