skip to Main Content

One of the codebases I work with consists of several thousand React components, some of which were written before we enforced proper design standards in place around things like use of exterior margins. It’s time to clean this up.

Detecting exterior margins should be simple enough: each time a component is rendered find its root DOM element(s), check the union of their border boxes against the union of their outer boxes, and record a diagnostic message if they differ.
(I know this is likely to be incredibly slow. It’d be activated by a runtime flag in debug builds.)

How might I run code to check this after each component’s rendered elements have been reflowed? The following constraints apply:

  • React version 17 (currently; due for update)
  • We use class and occasionally function components, connected with Redux. React Hooks are forbidden, JSX/TSX is forbidden.
  • Extending the Component base class is forbidden, as some components already implement lifecycle methods such as componentDidMount.

Ideally there’d be a way to introspect the current mounted component tree on-demand, but I’ve been unable to find one.

Alternatively, maybe there’s an existing way (plugin, etc) to identify components rendered with exterior margins?

2

Answers


  1. Chosen as BEST ANSWER

    The prohibition on extending Component is not as absolute as I believed. There is actually a way we can do this without breaking our codebase, by replacing methods in the constructor instead of overriding them as usual:

    constructor(props: Readonly<{ children?: React.ReactNode }> & Readonly<P>, context?: {}) {
        super(props, context);
    
        // ...
    
        if (Debug.Margins.enabled()) {
            const oldComponentDidMount = (this as any).componentDidMount;
            (this as any).componentDidMount = () => {
                oldComponentDidMount && oldComponentDidMount.bind(this)();
                checkComponentMargins(this);
            }
    
            const oldComponentDidUpdate = (this as any).componentDidUpdate;
            (this as any).componentDidUpdate = (prevProps: Readonly<{ children?: React.ReactNode }> & Readonly<P>, prevState: S) => {
                oldComponentDidUpdate && oldComponentDidUpdate.bind(this)(prevProps, prevState);
                checkComponentMargins(this);
            }
        }
    }
    

    Not nice, but works. Might be useful for someone else.

    However we still have the problem of identifying the root element(s) of the rendered component. The best I've found so far is findDOMNode but that only works for components with a single root element (ie. not Fragment) and it triggers warnings due to our use of strict mode.

    Good enough for now.


  2. This sounds like something you could do with a testing framework. Cypress or Nightwatch would probably do the trick. I believe they both render components in an actual browser, and you can run real JavaScript on the document.

    You could write a test that renders each component with a data-testid attribute. Then you can easily grab the root element of the component and pass it to a JavaScript function that gets the calculated margins. And the cherry on top is you can make the tests fail if the margins are wrong, which will give you some strong regression testing and prevent the need for manual testing.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search