You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, the same trait would be executed multiple times in a React update cycle. If there are side effects in the trait, some unexpected results may occur. So, this Issue discusses how to solve this problem.
Phenomenon
Let’s take a look the example of this problem. Here are a log trait, it just log the params value when they change.
As we can see, it logs twice when the dependent state only changes once. If traits implement the logic of side effects, they can cause unexpected errors, which should not be expected.
Solutions
Batch Execute For The Trait
We should probably wait until all of the Trait's properties have been updated in one React update cycle before executing the Trait. This avoids the problem of executing the Trait multiple times in one React update cycle.
// ImplWrapperMain.tsxconst[traitPropertiesMap,setTraitPropertiesMap]=useState({});// batch update the properties of traitsuseEffect(()=>{conststops: ReturnType<typeofwatch>[]=[];constproperties: Array<RuntimeTraitSchema['properties']>=[];c.traits.forEach((t,i)=>{const{ result, stop }=stateManager.deepEvalAndWatch(t.properties,({result: property}: any)=>{setTraitPropertiesMap({set(traitPropertiesMap,t.type,property);})},{
slotKey,fallbackWhenError: ()=>undefined,});stops.push(stop);properties.push(result);});setTraitPropertiesMap(c.traits.reduce((result,trait,i)=>{result[trait.type]=properties[i];returnresult;},{}));return()=>stops.forEach(s=>s());},[...]);// execute traits after the properties changed useEffect(()=>{c.traits.forEach((t,i)=>{consttraitResult=executeTrait(t,property);setTraitResults(oldResults=>{// assume traits number and order will not changeconstnewResults=[...oldResults];newResults[i]=traitResult;returnnewResults;});});},[...]);
But the problem with this solution is that it doesn't execute the Trait until the next iteration of React, which is a potentially risky change from the original mechanism.
Batch Execute For Side Effects
Maybe we don't need to do a batch update on the whole Trait, but only on the side effects. For example, we provide a batch execution method batchExecute for traits to perform side effects.
The final solution is that we don't deal with this issue at runtime, but instead ask users to write traits without executing logic that has side effects. If the users need to perform side effects, they can write components to do so.
Conclusion
I prefer the third option, which is to provide a new lifecycle function traitPropertiesDidUpdated to traits.
The reasons are as follows:
The first solution solves the problem, but it changes the original implementation mechanism and has the possibility of causing the original project to go wrong unexpectedly.
The second option does not break the original mechanism, but providing a single function to handle side effects may increase the user's understanding cost.
The third, more intuitive solution is to provide a new lifecycle function that executes when a Trait property changes, allowing users to do anything in it, including side effects that they don't want to repeat.
The last one requires users to modify their own code and is unfriendly because it doesn't address the limitations of traits.
The text was updated successfully, but these errors were encountered:
I prefer the first way although it is the hardest way. This problem not only occurs in trait, but also in component. If a component has multiple expressions which use the same state, it also will render multiple times.
The root cause is that we watch the changes in expression level, not in component property level. If we fix it as so, it will be a big change, which need more attention to see the influence.
The third way will not break current code, but it seems too temporary. I think we'd better check how big influence the first way will bring. If it is not as big as we think, we should go with it. If it is really very big, then we can go the third way.
Currently, the same trait would be executed multiple times in a React update cycle. If there are side effects in the trait, some unexpected results may occur. So, this Issue discusses how to solve this problem.
Phenomenon
Let’s take a look the example of this problem. Here are a log trait, it just log the params value when they change.
We pass the
{{state.value}}
and the{{!state.value}}
as the values of theparam1
and theparam2
. Here is the full App schema:After that we click the button to change the
state
‘s value once, and then check the logs:As we can see, it logs twice when the dependent state only changes once. If traits implement the logic of side effects, they can cause unexpected errors, which should not be expected.
Solutions
Batch Execute For The Trait
We should probably wait until all of the Trait's properties have been updated in one React update cycle before executing the Trait. This avoids the problem of executing the Trait multiple times in one React update cycle.
But the problem with this solution is that it doesn't execute the Trait until the next iteration of React, which is a potentially risky change from the original mechanism.
Batch Execute For Side Effects
Maybe we don't need to do a batch update on the whole Trait, but only on the side effects. For example, we provide a batch execution method
batchExecute
for traits to perform side effects.Add
traitPropertiesDidUpdated
Or we can provide a
traitPropertiesDidUpdated
lifecycle function to traits to perform side effects.Don’t Change The Code In Runtime
The final solution is that we don't deal with this issue at runtime, but instead ask users to write traits without executing logic that has side effects. If the users need to perform side effects, they can write components to do so.
Conclusion
I prefer the third option, which is to provide a new lifecycle function
traitPropertiesDidUpdated
to traits.The reasons are as follows:
The first solution solves the problem, but it changes the original implementation mechanism and has the possibility of causing the original project to go wrong unexpectedly.
The second option does not break the original mechanism, but providing a single function to handle side effects may increase the user's understanding cost.
The third, more intuitive solution is to provide a new lifecycle function that executes when a Trait property changes, allowing users to do anything in it, including side effects that they don't want to repeat.
The last one requires users to modify their own code and is unfriendly because it doesn't address the limitations of traits.
The text was updated successfully, but these errors were encountered: