Search code examples
reactjsanti-patterns

Is it an anti-pattern to use variables the outside of React function component?


Is below case an anti-pattern in React?

Variables and functions might be declared outside a React function component for some reasons. For example, an timer ID and others could be used as below:

import React from "react";

const myFunc = () => { // no purpose
    /*some codes*/
}
let myBool = false; // no purpose
let timerId = 0;
    
const MyComponent = () => {
    const [name, setName] = useState("");
    
    /* some codes */

    useEffect(() => {
        timerId = setTimeout(() => {
            /* some codes */
        }, 1000);
            
        return () => clearTimeout(timerId);
    }, []);
        
    return (
        <div>Hello world</div>
    );
}

export default MyComponent;

Solution

  • For variables that may change, such as in your example, yes, it could well be considered to be an issue, because you couldn't be able to count on any use of the component having a consistent effect, because it's not pure, but depends on the current state of an outer variable (and not React state).

    For example, if you had a MyComponent, and you later decided to add another section to your site that also used MyComponent, and both were rendered at once, your existing code would cause problems because both components would share the same timerId variable - the first component to render would have its timerId lost. (Only the second component to render would have the persistent outer timerId be clearable)

    In these sorts of situations, in which values used inside a component pertain to a given instance of that component, rather than the whole app, you should use state (with useState) rather than an outer variable.

    Using outer identifiers in general isn't always a bad idea, though - quite the contrary, for constant values that won't change and are useful to have abstracted away, such as URLs, API keys, separate non-React functions, and so on. The following sort of pattern is not uncommon:

    import { makeApiCall } from './makeApiCall';
    
    export const SomeComponent = () => {
      // ...more code here dealing with state
      const handleClick = () => {
        makeApiCall()
          .then(handleSuccess)
          .then(handleFail);
      };
      return (
        <button onClick={handleClick}>click</button>
      );
    };