Code Guidelines
General guidelines
Do not mark as required props that are not actually required for a component functionality, this also means that in most cases you do not need to require a property that is also not required by native html elements.
Leverage existing Primitive libraries, do not try to reinvent the wheel.
Do not use
any
Add the correct types to the component callback functions.
Avoid declaring components inside of other components.
Do not use nested ternary operators. An
if else
clause is easier to read and maintain.Use consistent casing.
If your component needs to place UI on top of other elements that you do not control, use React Portals.
Leverage a Primitive library
Use low-level UI component libraries as the base layer of the design system, this way we can avoid writing inaccessible and non-performant components.
To achieve this we use radix-ui. Example:
Don't reimplement or rewrite native html props
Leverage existing props from HTML or from the Primitives library.
Instead of writing
You can do this:
If you need to add new props:
If you don't support or need all the native HTML props:
Export a single file or object for a component
If your component has multiple parts that the user will have to compose, export them from a single object instead of having multiple exports.
Instead of:
Export:
Avoid using useEffect
The react docs explain it better than we can do, but here are some general guidelines:
If you can calculate something during render, you don’t need an Effect.
To cache expensive calculations, add useMemo instead of useEffect.
To reset the state of an entire component tree, pass a different key to it.
To reset a particular bit of state in response to a prop change, set it during rendering.
Code that needs to run because a component was displayed should be in Effects, the rest should be in events.
If you need to update the state of several components, it’s better to do it during a single event.
Whenever you try to synchronize state variables in different components, consider lifting state up.
You can fetch data with Effects, but you need to implement cleanup to avoid race conditions.
Transforming data for rendering in useEffect is inefficient:
When you update your component’s state, React will first call your component functions to calculate what should be on the screen. Then, React will “commit” these changes to the DOM, updating the screen. Then React will run your Effects. If your Effect also immediately updates the state, this restarts the whole process from scratch! To avoid the unnecessary render passes, transform all the data at the top level of your components. That code will automatically re-run whenever your props or state change.
Testing
To test we use react-testing-library, this allows us to write maintainable tests and avoid including implementation details. Test component behavior and ways the user can interact with the component. An example for the Input component would be the following tests:
can be rendered in an uncontrolled way
will trigger onChange when used as a uncontrolled component
will only trigger the onChange event if the user has interacted with it
onChange will not trigger with a disabled input
onChange will not trigger with a read only input
onChange will work properly with a debounced function
Examples for a Dropdown:
show options on click
show default value with no selection
if options are visible, hide them when user clicks outside
on selection, show selected option in dropdown trigger
Do not test implementation details, this means that you should avoid testing component inner logic like:
state values
hook runs
results of parser functions inside the component
Stateful components can be controlled or uncontrolled (borrowed from radix-ui)
Similar to form field JSX elements in React, all components with internal state can either be uncontrolled (internally managed) or controlled (managed by the consumer)
Components exist in a finite number of predefined states (borrowed from radix-ui)
State in this context refers to a component's state representable by a finite state machine; not to be confused with arbitrary stateful data as typically referenced in React libraries.
States are predetermined during the component design phase and expressed as strings in component code, making state transitions more explicit, deterministic, and clearer to follow.
Use the data-state attribute to expose a component's state directly to its DOM element.
When tempted to use a boolean to track a piece of stateful data, consider enumerated strings instead
Developer experience (borrowed from radix-ui)
Component APIs should be relatively intuitive and as declarative as possible.
Provide in-code documentation for complex/unclear abstractions for easier source debugging.
Anticipate errors and provide thorough console warnings with links back to documentation.
Last updated