How does React's useState change the value of a constant? - javascript

This is the syntax I'm working with.
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount((count + 1));
};
I understand that setCount is creating an instance of count, but I'm really not grasping how exactly count is being changed if it's a constant or how, if it's an instance, it's able to be called and return the most recent value.
Wouldn't every time React re-renders the page, it reads the constant count first?
It's working just fine for me, but I can't wrap my head around why.

count is 'constant' for the duration of the function. When setCount() is called, the local count doesn't change. Eventually your component gets rendered again with the new value.
During this new render the count is updated, but it will again be constant for the duration of the render/function.

Related

When to use callback useState

It's maybe simple question, but the i'm not fully understand useState
I have confuse with when to use callback with useState, say example, i have an hook
const[count,setCount] = useState(0)
We can easy to update, for example , if i want to update the number, i can do:
setCount(count+1)
But if we have some code like setInterval,we want to increase the count every 1 second, when do the same as above
setTimeout(()=>{
setCount(count+1)
},1000)
console.log(count)
It will not increase even 1s passed
But we do
setTimeout(()=>{
setCount((count)=>{count+1}
},1000)
console.log(count)
But why it work???
My question is
When to use callback in useState?
In React, when you update a state property, with respect to its previous value, you
should use,
setState((state,props)=>{ return doSomethingWith(state.property)})
In your code (using hooks):
setCount((count)=>(count+1))
This is due to the fact that, state updates may be asynchronous in React.
By the way setCount((count)=>(count+1)) is not the same as setCount((count)=>{count+1}). In the 2nd case, the curly braces form an empty statement, & hence count+1 is not returned. In lambda functions the auto-return happens only in the case of a single expression.
Whenever a current state value update depends on the previous state , we can use a callback function.
if we have some code like setInterval,we want to increase the count every 1 second
For setInterval, Each second we need to update the count based on the previous state.This will update the count every 1 second interval.
setInterval(() => { setCount((count) => count + 1); }, 1000);
You use the callback when you want to keep the previous state and add something to it.
For example, let's say we have a counter in your case
const [counter, setCounter] = useState(0);
If I want this value to increase every second or when I press a button, I would use the following:
setCounter(prev => (prev + 1);
// Or
setCounter(counter => (counter + 1);
prev will be the same value as counter doesn't matter what you call it.
And since you will be using setInterval you will have to do this operation in a useEffect hook is the reason you will need the callback
otherwise if you keep setting the value to counter + 1 without using the callback you will just be 0 + 1 all the time.
Unless you wanna use it for something like increasing a like button counts. In that case you don't need callbacks.
Try reading and understanding those articles and docs:
setInterval in react hooks
useEffect Hook

What's the difference between manipulating a prop using a constant vs. using a function?

In this approach, I'm checking if propOne is even and then assigning the result to a constant.
import React from 'react';
const TestComponent = ({ propOne }) => {
const isEven = propOne % 2 === 0
return (
<div>
{`Is even? ${isEven ? 'Yes' : 'No'}`}
</div>
);
};
In this other approach, I'm doing the same logic (checking if propOne is even), but instead of assigning the result to a constant, I'm assigning it to a function.
import React from 'react';
const TestComponent = ({ propOne }) => {
const isEven = () => propOne % 2 === 0
return (
<div>
{`Is even? ${isEven() ? 'Yes' : 'No'}`}
</div>
);
};
Besides the obvious difference of having to use isEven() instead of just isEven, what are the differences between these two approaches?
There shouldn't be any difference, given that this is a function component. (Which means using the function is adding a small amount of verbosity for no gain.)
But note that there would be a difference in a class component, if isEven referenced this.props.propOne and were used in (eg) an event handler that is called some time after the render - see this article by Dan Abramov (one of the React developers) for more on this.
The function approach will reevaluate the expression every time you access it. In this specific case, I don't see a lot of benefit since if propOne changes it will cause a re-render anyways.
Based on this specific example, just go with the non-function wrapper version

ReactJS variable update without the code to be executed

I'm getting out of my mind...
If someone can explain this to me:
componentDidUpdate(prevProps, prevState) {
let categoriesChange = false
console.log('check', JSON.stringify(this.checkedd), JSON.stringify(this.props.checked))
if (JSON.stringify(this.checkedd) !== JSON.stringify(this.props.checked)) {
console.log('did')
categoriesChange = true
this.checkedd = this.props.checked
console.log('didit', JSON.stringify(this.checkedd), JSON.stringify(this.props.checked))
}
let currentQuery = this.returnQueryString(prevProps.filter);
let nextQuery = this.returnQueryString(this.props.filter);
if (categoriesChange) {
if (nextQuery.send) {
this.props.refresh(nextQuery.queryString);
}
}
}
In this piece of code, the variable this.checkedd, which is instantiated with null in constructor, updates without entering in the JSON.stringify if.
Whenever Checked props(this.props.checked) is updated, the first console log shows them equal without entering in the if to show the "did" console.log.
Can someone explain how is this even possible? that a variable updates without the piece of code to be executed?
That's because objects are reference types in javascript. When you say, this.checkedd = this.props.checked, you are assigning the reference of the this.props.checked object to this.checkedd and not the actual value.
So when this.props.checked gets updated, this.checked will also reflect that change since they are both referring to the same object in memory.
Also, the way you are trying to achieve what you want is quite weird. You don't need to store the previous props value in an instance variable, i.e. this.checkedd. The componentDidUpdate hook has two parameters to it, prevProps and prevState. You could just check if the categories have changed by comparing the values in prevProps.checked and this.props.checked.

how to stop array.filter() function

Am using custom search filter
HtML
<input type="text" pInputText class="ui-widget ui-text" [(ngModel)]
="gloablFilterValue" (ngModelChange)="splitCustomFilter()" placeholder="Find" />
I am using ngModelChange() event on search box
globalSearch(realData, searchText, columns) {
searchText = searchText.toLowerCase();
return realData.filter(function (o) {
return columns.some(function (k) {
return o[k].toString().toLowerCase().indexOf(searchText) !== -1;
});
});
}
splitCustomFilter() {
let columns =
['PartNoCompleteWheel', 'DescriptionCompleteWheel', 'PartNoTyre', 'DescriptionTyre', 'PartNoRim', 'DescriptionRim','DeletedDateFromKDPStr', 'DateFromKDPStr', 'Status'];
this.tyreAndRimList = this.globalSearch(this.tyreAndRimList, this.gloablFilterValue, columns);
}
The this.tyreAndRimList list of values for the columns which is mentioned in a column variable.
Problem
The filter is working good! But the main problem is filter performance is very poor while the record count is huge(more than 100 rows per every column)
When
The filter is working good if am entering a single character (like a). But when I was typing the character continuously the browser is hanging. the reason is the filter has been firing every typing on the filter box(because of am using ngModelChange()// onchange() event)
What I want
I want to stop filtering if the user typing continuously on the search box. Once the user has stop the typing then only I need to start filtering.
What I did
I have tried to handle this by using setTimeout(). But it just wait the filter call for a second. It is working if the user entered just 2 or 3 character's continuously. But if the user typing more than 7 or 8 or above character's, it continues to hang the browser. because of many filter callbacks are processing on the same time.
setTimeout(() => //code of filtering ,1000);
Question
How to stop filtering while user continuously entering value and start the filtering once the user has been stop the typing?
I am working in angular-2 and typescript. But this question is not related with only for angularjs or angular or JavaScript or typescript because of I want an idea not a solution. So I'll add those all tags for this question. Don't remove it. Thanks
Debounce the function. See how underscore does it here: Function Debouncing with Underscore.js.
You would then generate a debounced version of your function like this:
var globalSearchDebounced = _.debounce(globalSearch, 100, false);
It will only call after the user has stopped typing for at least one second.
It's not possible to interrupt the Array.filter method. Based on what you need you could handle this like this:
let timerId = null
function handleChange() {
if(timerId) {
clearTimeout(timerId)
}
timerId = setTimeout(() => /* Array.filter(...) */, 1000)
}
Explanation
Have a variable which will contain the timerId returned from the setTimeout function. Every time the model get changed the handleChange function will be called (in this example). The function checks if the variable which contains the timerId is set and contains a timerId, when the variable contains the timerId the clearTimeout function will be called to clear the previous timeout after that the handleChange creates a new timeout and assigns the timerId (returned from the setTimeout function) to the variable.
Documentation for setTimeout
Documentation for clearTimeout
Without underscore, and without a Timeout (that will trigger the whole Angular lifecycle by the way), you can use an Observable with the async pipe and a debounce.
In your global search function :
return Observable.of(/* filter here and return the filtered array */).debounceTime(1000)
In your list (that has to be somewhere I guess)
<list-item *ngFor="let x of myFilteredResults | async">...</list-item>
I have complete it by using Subject to debounceTime.
private subject = new Subject<string>()
ngOnInit() {
this.subject.debounceTime(300).subscribe(inputText => {
this.gloablFilterValue = inputText;
this.splitCustomFilter(); // filter method
});
}
Now when I change the value in this.gloablFilterValue object by using change event. It just waiting for the end of event completion.

Batching functions synchronously?

This is a tricky problem. Is it possible to batch function calls without waiting for the next tick of the event loop?
Derived example for simplicity.
let numbers = []
let total = 0
const addNumber = (num) => {
numbers.push(num)
compute()
}
// for the sake of this argument let's say compute is very expensive to run
const compute = () => {
numbers.forEach((num) => {
total += num
})
numbers = []
}
This is currently what happens. compute runs after every addNumber because we need the total to be computed synchronously.
This is the example we need to work. We can only call addNumber we cannot directly call compute. Is there a way change addNumber so this example works but only calls compute once?
addNumber(2)
addNumber(4)
addNumber(10)
console.log(total === 16) // needs to be true
Thanks for any help!
No. What I'd suggest is move the placement of your compute function so it calculates the value of total just before you need to retrieve it.
Any other option I can think of would entail some kind of asynchronous functionality, which you've said you don't want.

Categories

Resources