# SEE 05.10.2022 Notes - Developer Agreements ## Overview / Current contex We have recently expanded our Alkemio Client configuration to have a working set of Lint rules based on airbnb guidelines. This covers many straightforward semantic verification cases, but doesn't help with more complex rules that can't easily be captured by a lint rule. Therefore, as a step towards our contributing guidelines, we need a set of written developer agreements about coding standards we would like to use and an agreed, publicly accessible location to access them (an MD). ## Goals of the meeting - Derive a set of rules to add in our client - Agree on follow-up steps ## Rules - [x] [Do not use mixins](https://facebook.github.io/react/blog/2016/07/13/mixins-considered-harmful.html) - [x] Always use JSX syntax - [ ] Do not use React.createElement unless you’re initializing the app from a file that is not JSX - [ ] If you don’t have state or refs, prefer normal functions (not arrow functions) over classes: ```types // bad class Listing extends React.Component { render() { return <div>{this.props.hello}</div>; } } // bad (relying on function name inference is discouraged) const Listing = ({ hello }) => ( <div>{hello}</div> ); // good function Listing({ hello }) { return <div>{hello}</div>; } ``` - [x] Extensions: Use .jsx extension for React components. - [x] Use PascalCase for filenames. E.g., ReservationCard.jsx. - [x] Component Naming: Use the filename as the component name. For example, ReservationCard.jsx should have a reference name of ReservationCard. However, for root components of a directory, use index.jsx as the filename and use the directory name as the component name: ```types // bad import Footer from './Footer/Footer'; // bad import Footer from './Footer/index'; // good import Footer from './Footer'; ``` - [x] Higher-order Component Naming: Use a composite of the higher-order component’s name and the passed-in component’s name as the displayName on the generated component. For example, the higher-order component withFoo(), when passed a component Bar should produce a component with a displayName of withFoo(Bar). > Why? A component’s displayName may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening. ```types // bad export default function withFoo(WrappedComponent) { return function WithFoo(props) { return <WrappedComponent {...props} foo />; } } // good export default function withFoo(WrappedComponent) { function WithFoo(props) { return <WrappedComponent {...props} foo />; } const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component'; WithFoo.displayName = `withFoo(${wrappedComponentName})`; return WithFoo; } ``` - [x] Props Naming: Avoid using DOM component prop names for different purposes. > Why? People expect props like style and className to mean one specific thing. Varying this API for a subset of your app makes the code less readable and less maintainable, and may cause bugs. ```types // bad <MyComponent style="fancy" /> // bad <MyComponent className="fancy" /> // good <MyComponent variant="fancy" /> ``` - [ ] Do not use displayName for naming components. Instead, name the component by reference. ```types // bad export default React.createClass({ displayName: 'ReservationCard', // stuff goes here }); // good export default class ReservationCard extends React.Component { } ``` - [x] Always use camelCase for prop names. ```types // bad <Foo UserName="hello" phone_number={12345678} /> // good <Foo userName="hello" phoneNumber={12345678} /> ``` - [x] Avoid using an array index as key prop, prefer a unique ID. > [Why?](https://robinpokorny.medium.com/index-as-a-key-is-an-anti-pattern-e0349aece318) ```types // bad {todos.map((todo, index) => <Todo {...todo} key={index} /> )} // good {todos.map(todo => ( <Todo {...todo} key={todo.id} /> ))} ``` - [ ] Always define explicit defaultProps for all non-required props. > Why? propTypes are a form of documentation, and providing defaultProps means the reader of your code doesn’t have to assume as much. In addition, it can mean that your code can omit certain type checks. ```types // bad function SFC({ foo, bar, children }) { return <div>{foo}{bar}{children}</div>; } SFC.propTypes = { foo: PropTypes.number.isRequired, bar: PropTypes.string, children: PropTypes.node, }; // good function SFC({ foo, bar, children }) { return <div>{foo}{bar}{children}</div>; } SFC.propTypes = { foo: PropTypes.number.isRequired, bar: PropTypes.string, children: PropTypes.node, }; SFC.defaultProps = { bar: '', children: null, }; ``` - [x] Use spread props sparingly. > Why? Otherwise you’re more likely to pass unnecessary props down to components. And for React v15.6.1 and older, you could pass invalid HTML attributes to the DOM. - Exceptions - HOCs that proxy down props and hoist propTypes ```types function HOC(WrappedComponent) { return class Proxy extends React.Component { Proxy.propTypes = { text: PropTypes.string, isLoading: PropTypes.bool }; render() { return <WrappedComponent {...this.props} /> } } } ``` - Spreading objects with known, explicit props. This can be particularly useful when testing React components with Mocha’s beforeEach construct. ```types export default function Foo { const props = { text: '', isPublished: false } return (<div {...props} />); } ``` - Notes for use: Filter out unnecessary props when possible. Also, use prop-types-exact to help prevent bugs. ```types // good render() { const { irrelevantProp, ...relevantProps } = this.props; return <WrappedComponent {...relevantProps} /> } // bad render() { const { irrelevantProp, ...relevantProps } = this.props; return <WrappedComponent {...this.props} /> } ``` - [x] Use arrow functions to close over local variables. ```types function ItemList(props) { return ( <ul> {props.items.map((item, index) => ( <Item key={item.key} onClick={() => doSomethingWith(item.name, index)} /> ))} </ul> ); } ``` - [x] Do not use underscore prefix for internal methods of a React component. > Why? Underscore prefixes are sometimes used as a convention in other languages to denote privacy. But, unlike those languages, there is no native support for privacy in JavaScript, everything is public. Regardless of your intentions, adding underscore prefixes to your properties does not actually make them private, and any property (underscore-prefixed or not) should be treated as being public. See issues #1024, and #490 for a more in-depth discussion. ```types // bad React.createClass({ _onClickSubmit() { // do stuff }, // other stuff }); // good class extends React.Component { onClickSubmit() { // do stuff } // other stuff } ``` - [x] react/no-is-mounted - [x] eslint: react/sort-comp - [x] react-hooks/exhaustive-deps ###### tags: `SEE`