* légÚrement édité
Dis moi c'est oĂč t'as mal ?
Ah oui
The jQuery Effectâą
Grooos fichiers
//bad
if ($('.toto').hasClass('is-active')) {
$('.titi').hide();
} else {
$('titi').show();
}
.toto
.toto.is-active
.titi
ES6, Webpack
Angular, Ember, Redux*, etc.
* C'est pas un framework mais c'est pas grave
React, Vue.js, etc.
Mocha, chai
JS
like a boss
ES6, λ
Le systĂšme de vue
reducer
: (state, action) ⌠state
Webpack, ESLint, Tests
JS
like a boss
Quelques petits trucs sympas* pour Ă©crire du meilleur code.
* Parmi pleins d'autres
Cet homme est fou đ±
Les navigateurs ne vont pas supporter ça avant 2038 !
Pas d'inquiétude !
Webpack va nous permettre de faire du transpilling
ES6 â ES5 đ
const
, let
class
'use strict';
//ES5
[1, 2, 3].map(function (i) {
return i * 2;
}); // [1, 4, 6]
//ES6
[1, 2, 3].map((i) => i * 2); // [2, 4, 6]
'use strict';
//ES6
const MyConstructor = function () {
//this est MyConstructor
this.arr = [1, 2, 3];
return this.arr.map((i) => {
//this est toujours MyConstructor
return i * this.arr.length;
});
};
MyConstructor(); // [3, 6, 9];
'use strict';
//ES5
for (var i = 0; i <= 21; i++) {
var j = i * 2;
}
console.log(j); //42 ÂŻ\_(ă)_/ÂŻ
//ES6
for (let i = 0; i <= 21; i++) {
const j = i * 2;
}
console.log(j); //ReferenceError: j is not defined đ
'use strict';
//ES6
const a = 1234;
a = 5678;
//TypeError: Assignment to constant variable
'use strict';
//ES5
//callback hell
doSomethingAsync(function () {
doSomethingElse(function () {
maybeSomethingElse(function () {
...
console.log('done');
});
});
});
//ES6
doSomething()
.then(doSomethingElse)
.then(maybeSomethingElse)
.then(() => console.log('done'))
.catch((err) => console.log(err));
'use strict';
const doSomethingAsync = (something) => {
return new Promise((resolve, reject) => {
doSomething(something, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};
doSomethingAsync('toto')
.then((result) => console.log(result))
.catch((err) => console.log(err));
// ./services/myService.js
'use strict';
export class Say extends Something {
constructor(msg) {
this.msg = msg;
}
saySomething() {
console.log(this.msg);
}
};
export getSomething = () => {
return fetch('http://my-api.io/poneys');
};
// app.js
'use strict';
import { Say, getSomething } from './services/myService';
const say = new Say('hello world');
say.saySomething(); //hello world
getSomething().then(doSomething);
Vous prendrez bien un peu de programmation fonctionnelle ?
f : input âŒoutput
vs
mauvaises pratiques jQuery
map
,
filter
,
reduce
'use strict';
const $form = $('.js-my-form'),
$cgu = $('.js-cgu');
//BAD
const checkAndSubmit = () => {
if ($cgu.prop('checked')) {
$form.submit();
}
};
checkAndSubmit();
//GOOD
const checkAndSubmit = ($form, $cgu) => {
if ($cgu.prop('checked')) {
$form.submit();
}
};
checkAndSubmit($form, $cgu);
'use strict';
const petitsPoneys = [ ... ];
const grandsPoneys = petitsPoneys.map(grandir);
const grandsPoneysRouges = grandsPoneys.filter(
(p) => p.color === 'red'
);
const famillePoney = grandsPoneys.reduce(
(famille, poney) => {
return `${famille} ${poney.emoji}`;
},
'Famille : '
); // 'Famille : đŽ đ đ
'use strict';
//BTW, don't use the for loop
const poneys = [ ... ];
//BAD
for (const i = 0; i < poneys.length; i++) {
const poney = poneys[i];
doSomething(poney);
}
//GOOD
poneys.forEach(doSomething);
'use strict';
const buildColorFilter = (color) =>
(poney) => {
return poney.color === color;
}
;
const
filterRouge = buildColorFilter('red'),
filterBleu = buildColorFilter('blue');
const
poneysRouges = poneys.filter(filterRouge),
poneysBleus = poneys.filter(filterBleu);
(Ăa doit faire cet effet lĂ )
Eh bah enfin !
ie. â framework, â modĂšle
Composants, JSX, Performance
data â· vue HTML
Exemple
import React from 'react';
import ReactDOM from 'react-dom';
class Example2_1 extends React.Component {
render() {
return (
<p>Hello world</p>
);
}
};
ReactDOM.render(
<Example2_1 />,
document.querySelector('.js-react-example2-1')
);
import React from 'react';
import ReactDOM from 'react-dom';
const Example2_2 = () => (
<p>Hello world</p>
);
ReactDOM.render(
<Example2_2 />,
document.querySelector('.js-react-example2-2')
);
props
Passer n'importe quoi au composant
class Example3_1 extends React.Component {
render() {
return (
<p>{this.props.text}</p>
);
}
};
ReactDOM.render(
<Example3_1
text="Petit poney"
/>,
document.querySelector('.js-react-example3-1')
);
const Example3_2 = (props) => (
<p>{props.text}</p>
);
ReactDOM.render(
<Example3_2
text="Petit poney"
/>,
document.querySelector('.js-react-example3-2')
);
const Example3_2 = (props) => (
<p
onClick={props.coucou}>
{props.text}
</p>
);
ReactDOM.render(
<Example3_2
text="Petit poney"
coucou={() => alert('coucou');} />,
document.querySelector('.js-react-example3-2')
);
ie. composition de composants
const Item = (props) => (
<li>{props.emoji}</li>
);
const List () => (
<div>
<p>Famille Poney:</p>
<ul>
<Item emoji="đŽ" />
<Item emoji="đ" />
<Item emoji="đ" />
</ul>
</div>
);
ReactDOM.render(
<List />,
document.querySelector('.js-react-example4-1')
);
const List = (props) => {
const poneys = props.poneys.map((p, i) => (
<Item key={i} emoji={p} />
));
return (<div>
<p>Famille Poney :</p>
<ul>{poneys}</ul>
</div>);
};
const poneys = ['đŽ', 'đ', 'đ'];
ReactDOM.render(
<List poneys={poneys} />,
document.querySelector('.js-react-example4-1')
);
state
L'Ă©tat du composant
const Item = ({ poney }) => (
<li>{poney.emoji} ({poney.color})</li>
);
const List = (props) => {
var poneys = props.poneys.map((p, i) => (
<Item key={i} poney={p} />
));
return (<div>
<p>Famille Poney :</p>
<ul>{poneys}</ul>
</div>);
};
const poneys = [{ emoji: 'đŽ', color: 'red' }, ... ];
ReactDOM.render(
<List poneys={poneys} />,
document.querySelector('.js-react-example5-1')
);
const FilterBar = ({ filter }) => (
<div>
<button
onClick={() => filter('all')}>
Tout
</button>
<button
onClick={() => filter('red')}>
Rouge
</button>
<button
onClick={() => filter('blue')}>
Bleu
</button>
</div>
);
class List extends React.component {
constructor() {
super();
this.state = {
filter: 'all',
};
}
_handleFilter(filter) {
this.setState({ filter });
}
...
...
render() {
var poneys = this.props.poneys
.filter((p) => {
return this.state.filter === 'all'
|| this.state.filter === p.color;
})
.map((p, i) => (
<Item key={i} poney={p.emoji} />
));
return (<div>
<p>Famille Poney :</p>
<ul>{poneys}</ul>
<FilterBar
filter={this._handleFilter.bind(this)}
/>
</div>);
}
};
props
vs state
props |
state |
|
---|---|---|
Pourquoi ? | Configuration | Ătat |
Présence | Habituelle | Rare |
Modifiable* | Non (immuable) | Oui** |
Utilisation | Tout le temps | Le moins possible |
* Du point de vue du composant
** Crée un nouveau render()
du composant
â Point de mi-parcours
â Mais ne rĂ©sout pas tous les problĂšmes
Que se passe-t-il si le mĂȘme jeu de donnĂ©es est utilisĂ© par plusieurs composants ?
Redux is a predictable state container for JavaScript apps.
redux.js.org
(C'est vite le bazar)
Data-flow unidirectionnel
Une seule source de vérité :
le state
*
reducer: (state, action) ⌠state
* immuable
/**
* @param {Object} props.poney
{ id: 1, emoji: 'đŽ', color: 'red',
checked: false }
* @param {Function} props.toggle
*/
const Item = ({ poney, toggle }) => {
return (<li>
<input
type="checkbox"
defaultChecked={poney.get('checked')}
onClick={() => toggle(poney)} />
<label>
{ poney.get('emoji') }
</label>
</li>);
};
const List = ({ poneys, togglePoney }) => {
const items = poneys.map((p, i) => (
<Item key={i} poney={p}
toggle={togglePoney} />
));
return (<div>
<p>Famille Poney :</p>
<ul>{ items }</ul>
</div>);
};
const Summary = ({ poneys }) => {
const n = poneys.reduce((count, p) => (
p.get('checked') ? count + 1 : count
), 0);
return (<p>
{ n }
{ n > 1
? 'poneys sélectionnés'
: 'poney sélectionné' }
</p>);
};
const App = (props) => (<div>
<List {...props} />
<Summary poneys={props.poneys} />
</div>);
const togglePoney = (poney) => {
return {
type: 'TOGGLE_PONEY',
poney,
};
};
Reducer: (state, action) ⌠state
(state, action) ⌠new state*
* Immutable.js
import { Map } from 'immutable';
/**
* @param {Object} state
* @param {Object} action
*
* @return {Object} New state
*/
const reducer = (state = Map(), action) => {
switch(action.type) {
...
default:
return state;
break;
}
};
import { Map, fromJS } from 'immutable';
const reducer = (state = Map(), action) => {
switch(action.type) {
case 'INIT':
return fromJS(action.state);
break;
default:
return state;
break;
}
};
import { Map, fromJS } from 'immutable';
const reducer = (state = Map(), action) => {
switch(action.type) {
...
case 'TOGGLE_PONEY':
const poneyIndex = state
.get('poneys')
.findIndex((p) =>
p.get('id') === action.poney.get('id')
);
return state.setIn(
['poneys', poneyIndex, 'checked'],
!action.poney.checked
);
break;
...
}
};
import { createStore } from 'redux';
const store = createStore(reducer);
store.dispatch({
type: 'INIT',
state: {
poneys: [{
id: 1,
emoji: 'đŽ',
checked: false
}, ... ],
},
});
2 types de composants
Présentation | Container | |
Pourquoi ? | Vue | Logique |
Redux ? | Non | Oui |
Data | props | Redux state |
import { connect, Provider } from 'react-redux';
import App from './components/App.jsx';
import togglePoney from './actions";
const mapStateToProps = (state) => ({
poneys: state.get('poneys'),
});
const AppContainer = connect(
mapStateToProps, //map store et props
{ togglePoney } //map actions et props
)(App);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.querySelector('.js-redux-example2')
);
Présentation vs containers
reducer: (state, action) ⌠state
ie. gérons tout ça comme des pros
.
âââ sass/
âââ js/
| âââ utils/
| âââ components/
| | âââ App.jsx
| | âââ Graph.jsx
| | âââ Box.jsx
| | âââ Button.jsx
| âââ services/
| | âââ Stats.js
| | âââ Auth.js
| âââ actions/
| | âââ graph_actions.js
| | âââ button_actions.js
| âââ test/
| | âââ Stats.js
| | âââ App.jsx
| âââ reducer.js
| âââ index.js
âââ index.html
.
âââ sass/
âââ js/
| âââ stats/
| | âââ components/
| | âââ services/
| | âââ actions/
| | âââ test/
| | âââ reducer.js
| | âââ index.js
| âââ catalog/
| | âââ components/
| | âââ services/
| | âââ actions/
| | âââ test/
| | âââ reducer.js
| | âââ index.js
âââ index.html
Inspectons tout ça !
Moins de bugs, dev plus facile, consitance du code, etc. đ
// .eslintrc.json
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"extends": "airbnb",
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "simple"],
...
},
}
(ESLint branché sur un gulp watch
)
TDD đ€
import reducer from '../reducer';
describe('reducer', () => {
it('handles INIT', () => {
...
});
it('handles TOGGLE_PONEY', () => {
...
});
});
import { expect } from 'chai';
import Api from '../services/api';
describe('API service', () => {
//if synchronous
it('returns 2 with returns2()', () => {
expect(Api.returns2()).to.equal(2);
});
});
import { expect } from 'chai';
import Api from '../services/api';
//mock with fetch-mock here
const poneys = [{ emoji: 'đŽ', color: 'red' }, ... ];
describe('API service', () => {
it(
'returns poneys with getPoneys()',
function (done) {
Api.getPoneys()
.then((result) => {
expect(result).to.deep.equal(poneys)
done();
})
.catch(done);
}
);
});
import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import Item from '../components/Item';
describe('Item', () => {
it('renders an item', () => {
const TEXT = 'Poney';
const wrapper = shallow(
<Item text={TEXT} />
);
expect(wrapper.find('li')).to.have.length(1);
expect(wrapper.find('label').text()).to.equal(TEXT);
});
});
import React from 'react';
import { expect } from 'chai';
import { shallow, render } from 'enzyme';
import Item from '../components/Item';
describe('Item', () => {
it(
'invokes callback when the poney is clicked',
function (done) {
const PONEY = { ... };
const checkCb = (poney) => {
expect(poney).to.equal(PONEY)
done();
};
const wrapper = shallow(
<Item poney={PONEY} toggleItem={checkCb}/>
);
wrapper.find('input').simulate('click');
}
);
});
mocha
Webpack
à intégrer dans grunt
/gulp
et c'est parti ! đ
JS
like a boss
đ Le framework JS made in Lengow đ
* On se comprend, hein
Un peu de lecture