How do i call setState multiple times (e.g. in a loop)? - react-hooks

I am building a react app which is executing a complicated web-assembly process. The web-assembly implements basically a loop, and each iteration i need to write something in a output-textfield. The value of the textfield is bound to a state called output.
The problem is, that when i call setOutput subsequently (in the loop), the textfield does only re-render after the last setOutput. (expected behavior of useState hook)
In the web-assembly, i am always setting all the lines, therefore i do not depend on the previous state...
I have worked out a workaround which is using a class component, because here i can await setState, and then calling the next setState.
I don't want to mix functional components with class components. Is there another way?
Thank you in advance!

You can use the functional form of setState, which accepts a function that updates the state. This way, you can call setState multiple times in the loop, and each time it will correctly update the state and trigger a re-render.
Here's an example:
// initialize output state to an empty string
const [output, setOutput] = useState('');
// loop through some data and update the output state
for (let i = 0; i < data.length; i++) {
setOutput(prevOutput => prevOutput + data[i] + '\n');
}
In the example, the loop updates the output state by appending some data to the previous output value and adding a newline character. The prevOutput parameter of the update function represents the previous state value. By using this parameter, you ensure that the updates are correctly applied in sequence, even if they are not batched together.
Note that using setState in a loop can be an expensive operation, as it triggers a re-render each time. You may want to consider batching updates with the useEffect hook or using a different approach, depending on your specific use case.

Related

Angular11 using function in *ngIf in the template is getting invoked many times. how to avoid

I have the following code. I am using a function in *ngIf and it is being called many times. How to fix this? Can anyone help me with this?
<button
type="button"
class="button primary"
*ngIf="canComplete() | async"
(click)="complete()"
>
<span *ngIf="!userCanCompleteAudit">Complete assigned sections</span>
<span *ngIf="userCanCompleteAudit">Complete audit</span>
</button>
canComplete() {
return this.service.sub$.pipe(
takeUntil(this.cancel$),
take(1),
map(
(responses) =>
responses.every((r) => r.valid)
)
);
}
By design, canComplete() will indeed get invoked many times. More precisely, it will get invoked for every change detection cycle. So the question now is, what is a change detection cycle, how does it relate to your problem, and how can we build a solution with knowledge about it?
Introduction to Change Detection
Whenever a user interacts with your app, be it by clicking any buttons on a page, or triggering an HTTP request for example, Angular by default, has to re-evaluate all template expressions in your component template (i.e. your HTML code). Or as we say it, a change detection cycle is triggered.
In your case, Angular will re-evaluate the following template expressions:
the value of canComplete() | async (1st template expression),
of !userCanCompleteAudit (2nd template expression),
and of userCanCompleteAudit (3rd template expression).
Why does Angular have to re-evaluate the values of these template expressions, you ask? So that when Angular determines that an expression's value has changed, Angular will then update the template view (basically the DOM) accordingly.
For example, if your 2nd template expression !userCanCompleteAudit evaluates from previously true to now false, Angular will then remove <span>Complete assigned sections</span> from the DOM, as per your logic. On the other hand, if Angular finds out that !userCanComplateAudit did not change after checking said expression's current value, Angular leaves that span element alone and keeps it displayed.
When is an Expression Considered "Changed"?
How does Angular decide when a template expression has "changed"? Simple. By default, Angular:
gets the template expression's old value,
evaluates its new value,
then performs a === against the expression's old and new values.
If they are not strictly equal, Angular marks the template expression as changed, and updates the template view accordingly.
Why Methods in Template Expression Execute Multiple Times
At this point, we now know that for every change detection cycle (e.g. user interactions), template expressions get re-evaluated as part of Angular's change detection mechanism. And there lies the problem.
For your 2nd template expression canComplete() | async, this means that:
For every change detection cycle, canComplete() | async is evaluated.
Every time canComplete() | async is evaluated, canComplete() method is called. (This in particular is the issue you're seeing.)
Every time canComplete() is called, a new observable object is returned from this.service.sub$.pipe(...).
Every time an observable is returned from canComplate(), a new subscription instance is made in the template via the async pipe.
For every subscriptions made, the pipeline is run (calling responses.every((r) => r.valid) every time).
How to Prevent Method From Executing Multiple Times
The answer to this is a case-to-case basis. Depending on the context of your code, you might have to apply at least one of the following:
Setting your component's change detection strategy to ChangeDetectionStrategy.OnPush.
Refactoring your method (that returns an observable) into a observable variable.
Subscribing from the controller instead of the template.
Most likely in your case, and without diving too much into details, your best bet is to apply bullet 3: subscribe from your component's controller.
// your component controller
#Component(...)
export class MyComponent implements OnInit {
...
canComplete = false;
ngOnInit() {
this.service.sub$.pipe(
take(1),
map((responses) => responses.every((r) => r.valid)),
takeUntil(this.cancel$)
)
.subscribe((canComplete) => this.canComplete = canComplete);
}
}
<button *ngIf="canComplete" class="...">...</button>

Mixpanel: Undo or delete an event

On MixPanel, I track an event like so:
mixpanel.track('Action A')
I allow visitors to undo their actions when filling out a sign-up form. I would like to be able to send another event to undo the previous event:
mixpanel.decrement('Action A')
However, the decrement function in Mixpanel is only available on user properties, not events. I don't have unique_ids on these events because it's server-side and triggered by anonymous users, but I would like the ability to increment and decrement an accurate count of Action A. How can I delete the initial event or decrement the count by 1?
There is no way to delete events that are ingested by Mixpanel with no unique_id's connected to them.
It is possible to hide them so they don't appear in reports, but that sounds like it will defeat the purpose of what you are trying to accomplish.
Mixpanel does have documentation on making an incremental super property, which is tied to events and not people. A super property is a property that is sent with every event. The method mixpanel.register() is what is used to create Super Properties, but it also allows values to be overwritten which is one way to build an incremental/decremental event property.
This unfortunately involves building a function, but it should serve as a workaround. If you are using JS the function would look something like:
//define the incrementing function
incrementer = function(property) {
value = mixpanel.get_property(property);
update = {}
//Ensure that 'value' has a type = number
if(value && typeof(value) == 'number') {
update[property] = value +1;
}
else {
update[property] = 1
}
mixpanel.register(update);
};
There is some documentation on this here.
I think this will involve a little bit of tweaking depending on your implementation, but let me know if that helps solve it.

Redux - Previous values for props/state showing briefly before new values loaded

I am setting up my React project with Redux for the first time, and am running into an issue.
I have a basic container, mapStateToProps, action creator, and reducer in place, but I'm having this issue where when I load a certain page, the previous prop values are getting loaded to the page before the correct values are fetched. It makes sense that this is happening, but I was wondering if there was a way to get around this so that upon loading this component / container that the values would get cleared. (Or if it'd just be better to use React state instead of Redux state for this specific use case? The rest of the pages make sense to use Redux, but this is the only case where this becomes an issue)
Say I have a page that shows some state of some item: somePage.com/items/
The component and container are set up like this (skeletal for the sake of example):
class SomeComponent extends React.Component<......> {
constructor(props) {
super(props);
props.dispatch(fetchItemDetail(params));
}
render() {
// use the stuff in props to display item's info
}
}
function mapStateToProps(state) {
return {
someItemDetail: state.someItemDetail.X;
someOtherInfo: state.someDetail.Y;
}
}
export const ItemDetailContainer = connect(mapStateToProps)(SomeComponent)
The action creator involves calling an API and returning the payload, and the reducer is just a standard reducer that sticks the result of the API call into the state.
Basically, this "works", but if I navigate from going into SomeComponent with a parameter for ItemX, and click a link to go to SomeComponent for ItemY, ItemX's information will show until the call to fetch ItemY's info completes, then ItemY's info will appear.
What's the recommended approach for handling this issue? It looks like I can't just clear the props upon construction because they are readonly. Any thoughts?
Self-answer:
I got something to work. Basically, I ended up just creating an action creator, clearItemDetail w/ CLEAR_ITEM_DETAIL type. When the reducer sees actions with CLEAR_ITEM_DETAIL, they would then "clear" that part of the state. (I had multiple things to clear, so my actions and reducers were a little more complicated than this though.)
I dispatched the clearItemDetail() action creator inside the componentWillUnmount() lifecycle method of the component, and it seems to be working now.
Not sure if this is the best route though.
It looks like fetchItemDetail is an asynchronous call. That being so, write a reducer for fetchItemDetail to clear ItemY's data. The reducer can coexist with the middleware to handle the asynchronous call.
case FETCH_ITEM_DETAIL:
return Object.assign({}, state, {ItemY: null});
break;
You can handle the null value in ItemY in your component to not show any data. Then, upon completion of the asynchronous call, assign the returned value (this sounds like it's already done as the ItemY data does appear when the call is complete):
case FETCH_ITEM_COMPLETE:
return Object.assign({}, state, {ItemY: [your item y data]});
break;

Multiple parallel Increments on Parse.Object

Is it acceptable to perform multiple increment operations on different fields of the same object on Parse Server ?
e.g., in Cloud Code :
node.increment('totalExpense', cost);
node.increment('totalLabourCost', cost);
node.increment('totalHours', hours);
return node.save(null,{useMasterKey: true});
seems like mongodb supports it, based on this answer, but does Parse ?
Yes. One thing you can't do is both add and remove something from the same array within the same save. You can only do one of those operations. But, incrementing separate keys shouldn't be a problem. Incrementing a single key multiple times might do something weird but I haven't tried it.
FYI you can also use the .increment method on a key for a shell object. I.e., this works:
var node = new Parse.Object.("Node");
node.id = request.params.nodeId;
node.increment("myKey", value);
return node.save(null, {useMasterKey:true});
Even though we didn't fetch the data, we don't need to know the previous value in order to increment it on the database. Note that you don't have the data so can't access any other necessary data here.

Deciding whether or not a run a function, which way is better?

I have some data being loaded from a server, but there's no guarantee that I'll have it all when the UI starts to display it to the user. Every frame there's a tick function. When new data is received a flag is set so I know that it's time to load it into my data structure. Which of the following ways is a more sane way to decide when to actually run the function?
AddNewStuffToList()
{
// Clear the list and reload it with new data
}
Foo_Tick()
{
if (updated)
AddNewStuffToList();
// Rest of tick function
}
Versus:
AddNewStuffToList()
{
if (updated)
{
// Clear the list and reload it with new data
}
}
Foo_Tick()
{
AddNewStuffToList();
// Rest of tick function
}
I've omitted a lot of the irrelevant details for the sake of the example.
IMHO first one. This version separates:
when to update data (Foo_Tick)
FROM
how to loading data (AddNewStuffToList()).
2nd option just mixing all things together.
You should probably not run the function until it is updated. That way, the function can be used for more purposes.
Let's say you have 2 calls that both are going to come and put in data to the list. With the first set up, checking the variable inside of the function, you could only check if one call has came in. Instead, if you check it in the function that calls the data, you can have as many input sources as you want, without having to change the beginning function.
Functions should be really precise on what they are doing, and should avoid needing information created by another function unless it is passed in.
In the first version the simple variable check "updated" will be checked each time and only if true would AddNewStuffToList be called.
With the second version you will call AddNewStuffToList followed by a check to "updated" every time.
In this particular instance, given that function calls are generally expensive compared to a variable check I personally prefer the first version.
However, there are situations when a check inside the function would be better.
e.g.
doSomething(Pointer *p){
p->doSomethingElse();
}
FooTick(){
Pointer *p = new Pointer();
// do stuff ...
// lets do something
if (p){
doSomething(p);
}
}
This is clumbsy because every time you call doSomething you should really check you're
not passing in a bad pointer. What if this is forgotten? we could get an access violation.
In this case, the following is better as you're only writing the check in one place and
there is no extra overhead added because we always want to ensure we're not passing in a bad pointer.
doSomething(Pointer *p){
if (p){
p->doSomethingElse();
}
}
So in general, it depends on the situation. There are no right and wrong answers, just pros and cons here.

Resources