Jared Forsyth
that you can fix the bugs you find
sleep peacefully w/ tools in place to catch bugs
from a codebase that's clean & easy to maintain
with devtools
with linting, types, and tests
with Good Practicesβ’
(tip: install babel-plugin-transform-react-jsx-source)
with React Devtools
Just add Processβ’
π Our users are great π
const {cursorContextPropType, keyIdPropType} = require('./prop-types');
const ExpressionKeypad = React.createClass({
propTypes: {
// basic propType
currentPage: React.PropTypes.number.isRequired,
// shared proptype definition, imported from `./prop-types.js`
cursorContext: cursorContextPropType.isRequired,
dynamicJumpOut: React.PropTypes.bool,
// complex prop type, using an imported definition
extraKeys: React.PropTypes.arrayOf(keyIdPropType),
roundTopLeft: React.PropTypes.bool,
roundTopRight: React.PropTypes.bool,
},
with eslint
{
"rules": {
// ---------------------------------------
// ES6 and jsx rules.
"arrow-spacing": 2,
"computed-property-spacing": 2,
"constructor-super": 2,
"no-const-assign": 2,
"no-this-before-super": 2,
"no-var": 2,
"prefer-const": 2,
"prefer-spread": 2,
"react/forbid-prop-types": [2, { "forbid": [ "array", "object" ] }],
"react/jsx-closing-bracket-location": [2, "line-aligned"],
"react/jsx-curly-spacing": [2, "never"],
"react/jsx-indent-props": 2,
"react/jsx-no-duplicate-props": 2,
// This triggers a ton on stuff like 'if (window.x) { x(...) }'.
"react/jsx-no-undef": 2,
"react/jsx-sort-prop-types": 2,
"react/jsx-uses-react": 2,
// (Maybe we can be more strict and not need allow-in-func?)
"react/no-did-mount-set-state": [2, "allow-in-func"],
"react/no-did-update-set-state": 2,
"react/no-direct-mutation-state": 2,
"react/prop-types": 2,
"react/self-closing-comp": 2,
"react/sort-comp": 2,
// ---------------------------------------
// ES6/jsx stuff we explicitly disable.
// We turned this off since it was too much work for too
// little benefit, especially for one-line props.
"react/jsx-sort-props": 0,
// We turned this off because it complains when you have a
// multi-line string, which I think is going too far.
"prefer-template": 0,
// We've decided explicitly not to care about this.
"arrow-parens": 0,
"prefer-arrow-callback": 0
},
"ecmaFeatures": {
"arrowFunctions": true,
"blockBindings": true,
"classes": true,
"destructuring": true,
"experimentalObjectRestSpread": true,
"forOf": true,
"jsx": true,
"restParams": true,
"spread": true,
"templateStrings": true
},
"plugins": [
"react"
],
"env": {
"node": true,
"browser": true,
"es6": true
},
"extends": "./eslintrc.shared"
}
Catching simple mistakes
import React from 'react'
const NoUndef = React.createClass({
render() {
return <Component /> // Oops, forgot to import!
}
})
Catching simple mistakes
<SomeComponent
height={200}
items={theItems}
width={500}
profileUrl={this.props.profileUrl}
showHeader={false}
items={null}
responsive={this.props.flags.responsive}
/>
Flow or TypeScript
What bugs will they catch?
const myComplexFunction = require('../lib/my-complex-function');
// better make sure we cover these edge cases
assert.equals(myComplexFunction({edge: true}), 100);
assert.equals(myComplexFunction({edge: false}), 10);
assert.equals(myComplexFunction({edge: false, corner: true}), 780);
complex logic doesn't belong in components
put it in a pure function!
// we better render that signup button
assert.exists($('#signup'));
assert.isFunction($('#signup').onclick);
// ... ad nausium
prevent regressions with less hassle
// exercise-progress-phone-ui.jsx.fixture.js
module.exports = {
instances: [{
"attempts": [
{
"correct": true,
"seenHint": false,
"timeDone": "2015-12-01"
},
{
"correct": false,
"seenHint": false,
"timeDone": "2015-12-01"
}
],
"isSkillCheck": true,
"criterion": {
"type": "num_problems",
"numRequired": 10
},
"answerStatus": "correct"
}, ...]
}
// signup-page-test.js
import renderer from 'react-test-renderer';
test('Signup page renders correctly', () => {
const tree = renderer.create(
<SignUpPage />
).toJSON();
expect(tree).toMatchSnapshot();
});
<!-- signup-page-test.js.snap -->
<div
className="SignUpPage"
onClick={[Function]}
>
<input
placeholder="Name"
...
/>
...
<button
onClick={[Function]}
>
Sign up!
</button>
</div>
- expected
+ actual
<div
className="SignUpPage"
onClick={[Function]}
>
+ <div>
+ My new feature thing
+ ...
+ </div>
<input
placeholder="Name"
...
/>
...
- <button
- onClick={[Function]}
- >
- Sign up!
- </button>
</div>
β¬ yup, we added this
β¬ oops!
with Good Practicesβ’
handleClick() {
this.setState({clicked: true});
},
render() {
return <div style={...}>
<button onClick={this.handleClick}>
click me
</button>
</div>
}
...
handleClick() {
this.setState({clicked: true});
this.props.onClick();
},
...
...
getClickedState() {
return this.refs.child.hasBeenClicked();
},
...
Jared Forsyth