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.
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.
Related
Ok, here's a puzzler. I have a select input and I'm using Zustand for state. I'm seeing inconsistent state and not getting something I suspect.
It looks like this.
const handleSortChange = async (event) => {
// set a state variable
setSort(event.value)
// do a network call using sortBy and other things
// the call is wrong when using sortBy and I'll replace
// this bug with console.log statements below ...
}
There are two values for the select: "one" and "two".
If I console.log this stuff out, I see the problem and the bug. I can't use the state variable inside of this function. It doesn't await, resolve or behave the way I think it will.
So for my select, if I flip between one and two, I get this funny behavior:
const handleSortChange = async (event) => {
// set a state variable
setSort(event.value) // this sets sortBy
console.log(event.value)
console.log(sortBy) // this is the zustand state variable that is in scope
// I expect these would be the same, but they aren't! :O
}
The console.log output looks like this when switching from "one" to "two" on the select input.
two // event.value
one // the in-scope zustand variable sortBy as a read after the set
When switching to "two" on the select, I get the opposite but these variables aren't the same?
one // event.value
two // the set variable sortBy
When switching to "one" on the select. Because something isn't consistent or resolving like I think it is.
I thought that the zustand state variable would be consistent (especially when I add await and eslint is telling me that await does have effect for this function). This isn't an issue for me right now because I can use the parameter for everything I need. But I just feel like I'm missing something big here and I hope that Zustand isn't going to gotcha me when I need to rely on a state change or consistent store somewhere.
This seems to be the same issue and behavior that React has with setState. With setState in React you wouldn't do this even though this is a common trap. The value is not updated immediately, this way of thinking does not work for a concurrent GUI.
https://twitter.com/acemarke/status/1389376376508227592
In the case of Zustand, it might not even have a callback function to fire after set is called. In other words, this isn't going to work at this time.
I'm new to react and as I'm looking at examples, I see that a lot of tutorials don't use this.props or this.state directly. Instead, they will set a const at the beginning of the function. I've used the two interchangeably without seeing any difference in functionality.
Are there any gains to using const?
ie.
const {
error
} = this.state;
{error && <p>{error.message}</p>}
vs
{this.state.error && <p>{this.state.error.message}</p>}
This is called Destructuring, a feature of ES6.
There are no differences in functionality but there are little benefits of using destructuring.
First, cleaner code. If you destructure an object at the top of your code block, it appears more clear to the reader what variables you are going to use.
Second, it puts object properties in local variables which can improve performance, especially if you are using the variables multiple times like in a loop.
As you have yourself stated that there is no difference at all and it is true. My simple answer of using const is
After u define this way
const {error} = this.state;
now u can just use term (error) in the following codes instead of lengthy (this.state.error)
So it is just for simplicity
I'm giving myself a bit of a crash course on JavaScript so that I can learn React. I haven't done any dev work for many years so this may be a facepalm moment.
I am reading a blog post on useState(). I understand that if the parameter passed to useState() is a function, the function is only executed on the first render execution.
(EDIT: blog post is here https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/)
However, this confuses me:
const Message= () => {
const messageState = useState( () => expensiveComputation() );
/* ... */
}
In this example, why not just pass expensiveComputation to useState() like so?
const Message= () => {
const messageState = useState( expensiveComputation );
/* ... */
}
There are also some weird niggles about functions in Javascript that I don't think I'm understanding. For example, I've heard that arrow functions are executed when they are defined, but I've played with JSBin and I can't manage to prove that to myself in any way.
Thanks for the help!
I think these are the same in this particular case. The more general issue is the binding of this.
() => this.foo()
and
this.foo
are not the same, because "this" stays attached to the class in the first case and gets lost in the second case, so that when "foo()" is called in the second case, it won't be accessing the correct object as its "this".
It's generally regarded as a major design mistake of Javascript that this can be easily lost. The older-JS way to preserve this is with the bind method, i.e. this.foo.bind(this). But in language design, it's not possible to redefine the meaning of code without breaking everyone's existing applications. When lambdas with => were introduced, this was handled in the more modern, lexical way that preserves this.
Let's say I have a class component that has something like this:
export class Math extends React.Component {
...
someComponentMethod = numb => {
const sample = numb * 10
...
const result = numb -5
return result
}
Is it possible to make test assertions on sample variable in Jest?
It is not possible to write assertions for the private internals of functions.
I've personally found this barrier to encourage better tests to be written. By testing only the public API for correctness, you have the ability to refactor internals without having to update any tests. The existing tests continue to enforce that internal changes work correctly.
Writing tests against internal behavior easily increases the maintenance effort of the code being tested. Since the tests become more tightly coupled to the source code, they'll need more attention when making changes. This can also degrade the reliability of the tests, since more changes to the tests increases the likelihood of bugs manifesting in the tests themselves.
If you find yourself wanting to test some internal behavior, it might be a good time to extract some functionality. In your example, the sample value calculation could be extracted into a pure function of its own:
export class Math extends React.Component {
...
computeSampleValue(input) {
return input * 10
}
someComponentMethod = numb => {
const sample = this.computeSampleValue(numb)
...
const result = numb -5
return result
}
}
You can now assert that whatever logic is being used to calculate the sample value works as expected for various inputs. This extraction of logic often makes someComponentMethod more readable as well.
If you absolutely need to test some other internal behavior and are aware of the increased code debt, you can have your method perform side effects and write assertions for those. For example, instead of defining sample as a function-scope variable, create a this.sample property on the component instance and update that. In a test you then call the target method, and afterwards assert that componentInstance.sample was changed as expected.
I've managed to test a certain variable value inside a function the following way:
sum1.js
function sum() {
result = 1 + 2;
};
module.exports = sum;
sum1.test.js
const sum = require('./sum1');
sum();
test('adds 1 + 2 to equal 3', () => {
expect(result).toBe(3);
});
Hope it helps, cheers!
An article I was reading gives this as an example of an impure function (in JavaScript):
const tipPercentage = 0.15;
const calculateTip = cost => cost * tipPercentage;
That struck me as a bit of an odd example, since tipPercentage is a constant with an immutable value. Common examples of pure functions allow dependence on immutable constants when those constants are functions.
const mul = (x, y) => x * y
const calculateTip = (cost, tipPercentage) => mul(cost, tipPercentage);
In the above example, correct me if I'm wrong, calculateTip would usually be categorised as a pure function.
So, my question is: In functional programming, is a function still considered pure if it relies on an externally defined constant with an immutable value, when that value is not a function?
Yes, it is a pure function. Pure functions are referentially transparent, i.e. one can replace the function call with its result without changing the behaviour of the program.
In your example, it is always valid to replace e.g. calculateTip (100) anywhere in your program with its result of 15 without any change in behaviour, hence the function is pure.
Yes, theoretically, a function that abides by these two rules:
not causing side effects
not depending on "any mutable state"
can be considered pure, and as #TheInnerLight has put it well, provides referential transparency among other benefits. That makes the function in your example pure.
However, as the question is about & tagged with javascript, it is important to note that a function that depends on a const defined in the outer scope cannot always be considered pure:
const state = {}
function readState (key) {
return state[key]
}
// A hypothetical setState() can mutate `state` and
// therefore change the outcome of `readState` calls.
That is obviously because state value is not immutable (only the assignment is).
The rule of thumb is to prefer local over global when it comes to scopes. That provides better isolation, debuggability, and less cognitive load for the next reader.