Dongwook Kim
Dongwook Kim

Reputation: 79

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;

Upvotes: 2

Views: 4237

Answers (3)

Muhammad Ali
Muhammad Ali

Reputation: 2648

Yes, it is considered as anti-pattern and you can't just define these variables inside your component as the value will be re-initialized on re-rendering of the component. We have ref objects in react to achieve this:

import React from "react";

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

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

export default MyComponent;

myBool and timerId won't change on re-renders, you can mutate these objects as required. You can read more here.

Upvotes: 2

hrithik gautham TG
hrithik gautham TG

Reputation: 470

It is not a crime to do this. But before you do this, think why you wanna do this.

If you want to expose these variables along with the component to the outside world like export { MyComponent, myFunc, timerId, myBool};, then it makes sense to use this pattern. Or else it's best to declare the variable/function inside the functional component itself.

And Yes, it is an anti-pattern. IMO the whole idea behind functional components is to make the components pure. Making react functional components pure, makes it more predictable, where your output is only determined by the input to the function. using variables outside function's scope makes the function no longer pure. There are several benefits of using pure functions. i highly recommend using pure functional components.

https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976

Upvotes: 3

CertainPerformance
CertainPerformance

Reputation: 371138

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>
  );
};

Upvotes: 5

Related Questions