javascriptreactjsreact-nativereact-functional-component

React Native const vs let vs var in Functional Components


This is a broader question about the use of different variable declarations in react native functional components (as opposed to class components), specially the use of let vs const, and how they are effected by renders and such. For this question, when I say 'render' or 're-render' I am talking about the execution of the render function (like 'MyFunctionalComponent' below), not necessarily a UI update/change.

From my understanding, unlike plain js, a const variable is not exactly 'constant' in react/react native, and can be changed (with hooks?) like so:

export default function MyFunctionalComponent() {

    const [test, setTest] = useState(false);

    const testFunction = () => { //some sort of function, maybe called by a button press
        setTest(true); //this can change test, but something like 'test = true' throws an error
    }
}

However let can take on similar behavior from my understanding:

export default function MyFunctionalComponent() {


    let test = false

    const testFunction = () => { //some sort of function, maybe called by a button press
        test = true;
    }
}

However, most react native examples and tutorials and such I have looked at, always seem to use the const syntax, even though it seems to involve much more code. Why? Personal preference, or necessity? Does the const way of doing things somehow re-render/re-call MyFunctionalComponent with a new value for the const variable?

Next, I'm not sure the exact behavior that is causing this, but sometimes when using the const syntax inside the functional component, the variables change and save state between render calls, and sometimes, a const variable is reset to its default state every time the render is called. (I know this part is vague, so feel free to ignore it if there is not enough detail) Why does this happen?

Similarly, I have seen different behavior when consts are created outside of the functional component instead of within... does scope work as you would expect with these? Are they (still?) re-instantiated on new render calls? Does 'setState' call an re-render? Shortening the previous question, why do some of my const's preserve their state on re-renders, while some seem to reset to their defaults?

Where does var fall into this, does it act the same as in regular js, or is it affected by react as well? (I feel like I have a grasp of the differences in these variable declarations in regular js, but this const behavior with react making me question it all).

So overall the question is basically, what are the differences/advantages of let, var, and const, specifically in react/react native, and how do those differ from regular javascript?

Lastly, does it differ between class components and functional components in react native?

Thank you for reading this long question.


Solution

  • Personal preference, or necessity?

    Preference and style only.

    Using const to declare a stateful variable instead of var or let makes the intent of the code clearer. It's not necessary - pretty much all components you see would work just as well if they used var or let - but it introduces a slight possibility of confusion from those reading the code.

    React is written in JavaScript. Stateful variables in React, declared with const, cannot be reassigned, just like in ordinary JavaScript. The key you're missing is that the component function is called again every time there's a re-render, resulting in the call of the useState function returning a different value.

    For quick example of how this could work in vanilla JS with const and calling a function multiple times:

    let i = 0;
    const getI = () => i;
    const fn = () => {
      const theValue = getI();
      console.log(theValue);
      i++;
      setTimeout(fn, 1000);
    };
    fn();

    It's not that the variable gets reassigned (which would be forbidden due to the use of const), it's that the whole function runs again, resulting in a new value being assigned to the variable declared with const at the moment of its new initialization.

    Next, I'm not sure the exact behavior that is causing this, but sometimes when using the const syntax inside the functional component, the variables change and save state between render calls, and sometimes, a const variable is reset to its default state every time the render is called. (I know this part is vague, so feel free to ignore it if there is not enough detail) Why does this happen?

    You may be referring to the stale closure issue. This can happen if the variable binding that results in a difference being seen is from a prior render, rather than the current render.

    For a quick example of what that might look like in vanilla JS, adapting from the above snippet, I'll add a timeout on the first render, which will result in only the i from the first render being used:

    let i = 0;
    const getI = () => i;
    const fn = () => {
      const theValue = getI();
      console.log(theValue);
      if (theValue === 0) {
        // first render
        setTimeout(() => {
          console.log('Timeout from first render running, sees a theValue of:', theValue);
        }, 5000);
      }
      i++;
      setTimeout(fn, 1000);
    };
    fn();

    Similarly, I have seen different behavior when consts are created outside of the functional component instead of within... does scope work as you would expect with these? Are they (still?) re-instantiated on new render calls?

    Depends on what block that variable is in. If it's in another component, it may get re-initialized. If it's not in another component, then it might not be. For example, it's common to have a module that exports a component with some absolutely unchanging const values that all the components use declared up top, eg

    const apiKey = 'someApiKey';
    export const ApiInterface = () => {
      // ...
    };
    

    With regards to const vs let and var specifically, the problem with let and var is that they permit reassignment - but the only right way to change a stateful variable in React is to call the state setter, not to reassign the variable. Calling the state setter is what will result in a re-render; reassigning a variable will not result in a re-render, and reassigning a variable will result in the assigned value being lost on the next re-render. So, it's a good idea to be perfectly clear that stateful variables should not be reassigned.

    Lastly, does it differ between class components and functional components in react native?

    Yes, in class components, state is held as a property of the instance (the this). The state is no longer a standalone const identifier, but a property of a larger object, so there's no const vs let vs var for stateful values in class components - unless you extract values from state and put them into normal standalone variables yourself, in which case they behave just like any other standalone variable.