IV. Components - 24CaretSolutions/2b_n4tive GitHub Wiki
The chart represents 'parent-child' relationships between all components and scenes. It aims to visualize application structure and help to reuse any of its part.
The editable chart is available by the link. Feel free to edit the chart when committing any changes to the tree structure
- use nouns and adjectives to name a component; name should reflect component's purpose and what UI element will be eventually rendered on the screen (button, toggle, bar, list, etc)
- use camelcase for all component's names with first capital letter (good example - MyAwesomeThing, bad examples -
myAwesomeThing,my_awesome_thing) - if a component is a child of another component or scene, don't include parent name in the child component name (eg if there is submit button component inside awesome form component, a good name example for the first one could be SubmitButton, bad example -
AwesomeFormSubmitButton) - try to keep component's name short, but don't use contractions if it is really necessary (SubmitButton is more preferable than SubmitBtn)
When writing js-classes we rely on stage-3 proposal about class field declaration. So, to create an initial state of a component just simply add default value to the state variable inside class:
state = {
foo: 'bar'
};
instead of setting initial state inside the constructor of the class:
constructor(props) {
super(props);
this.state = {
foo: 'bar'
}
}
Use local state only to store any data that is involved in rendering of UI and specific to a single component and its close relatives (parent, children).
Use component instance (this.<something>) to store values for which a change should not trigger a re-render.
Use central store (Redux) to for keeping application state rather than UI state.
- use verbs to name a method of a component, not just nouns and adjectives (correct - displayDateOfBirth, incorrect -
DateOfBirth) - try to write methods as short as possible with maximum number of lines about 30-40 lines; if it takes more than 40 lines of code, it is a good practice to split a method into several functions that represents a completed logical step of code flow
- add empty line after every method code block except the last one
- part of some methods or even the whole method (eg static methods) can be treated as local utility function if it doesn't rely or modify component's property or state; try to identify these piece of code and put them into separate file in the component's folder; that will reduce size of the component and improve code readability
- use prefix
handleand name of UI element where event happens for all method names that handles DOM-events; the name pattern could be described as'handle' + <name of UI element> + <name of DOM event>(eg, for change event of search input: correct - handleSearchInputChange, incorrect -onInputChange; render block will look like<SearchInput onChange={this.handleSearchInputChange} />); name of UI element could be omitted if it is clear from the context what node triggers an event (eg component renders only one element) - use arrow function as a oppose to regular function that has to be bound to component context; see full explanation here
For all presentational components (see below) add prop types validation.
- instead of 'object' type use 'shapeOf' and describe all properties that the object has
- default prop types should be declared for all optional props; in functional components don't default props to primitives, use default prop types instead
- for frequently used large piece of data it is a good idea to store prop types centrally in the services folder (
/services/propTypes) and export them when necessary
We divide components into presentational and container components following the pattern which is described here.
Some rules for container components:
- if you don't use any component's lifecycle methods, export connect utility call directly without creating wrapping component, for example
export default connect(mapStateToProps, { <actionCreators> })( <Component> ) - use mapStateToProps function to add necessary store state to connected component props, for example
export const mapStateToProps = state => ({ user: state.data.users.current }); - we don't usually use mapDispatchToProps function; instead just pass necessary action creators as properties of object of the second parameter of connect function, for example
import { updatePageHeading } from 'App/store/screen/page/actions'; export default connect(mapStateToProps, { updatePageHeading })( HomePageContainer );
Don't forger to add displayName property to all higher order components. That will make debugging a bit more easier. Here is an example for withToggle component:
function withToggle(Component) {
function Wrapper(props) {
...
return <Component {...props} />
}
Wrapper.displayName = `withToggle(${Component.displayName || Component.name})`;
return Wrapper;
}