Possible navigation issue in React Native/Redux app - performance

During navigation in big React Native app with using Redux all visited scenes (scenes from navigation stack) are staying mounted. All these scenes receive props and get rendered in the order they were visited when any action is dispatched from last scene component. It causes freezes and visible delays between dispatching and last scene rendering.
For navigation I am using react-native-router-flux but same issue happens with original React Native Navigator too.
Video Possible navigation issues in React Native/Redux app
Code react-redux-navigation-test
Would be nice to know how to prevent passing props to not focused components from the navigation chain.
At the moment I am checking in shouldComponentUpdate of each component if this one is focused(visible) and return false in opposite case.
Is there any better solution?

I recommend that you calculate the scene distance between the current scene and scene being rendered using the navigation index in the scene and header rendering methods. If the distance > 1 return null.
This should prevent rendering the entire navigation history. It doesn't make any sense to me that this kind of behaviour is not available out of the box, but there it is.

I have not used the react-native-router-flux, however I do have experience with a fairly large React Native and Redux app. I have noticed that sometimes if the data you are working with gets large enough it can cause noticeable delays, but those are mostly limited to working in development. When the app is built it is usually noticeably faster. It seems as though Redux does things a little differently in development and production modes.
Regarding scenes in the navigation stack still being mounted, that is by design. Even when you navigator.push to another screen those previous screens remain mounted until they are either popped off the currentRouteStack or replaced from the currentRouteStack.
Also, it's probably worth noting that your simulator is in Slow Animations mode. Not sure if you did that for the video sake or not, but the navigation slowness is in part due to that. Just a heads up in case that wasn't on purpose.
I would check how the app seems to function after you've built it and it's not in development mode before troubleshooting the performance issues much further. May not be an issue for the final app product.

I'm going to go out on a limb here but are you using one of the below methods to prevent re-rendering?
PureComponents (added in React 15.3 I think)
Manually with shallow compare props and state with shouldComponentUpdate method.
By default React will re-render all components upon update unless you correctly handle the shouldComponentUpdate.
I'm doing something similar and not having these issues at all

We solved it by simply wrapping all screens in a component.
We have one ScreenView component that wraps the whole screen, and we pass it a parameter "restrictRerendersToRoutes".
This screen is connected to the state, and we update the currentScrene in our state and expose it to this screen view.
Then we simply restrict rerenders when the screen is in the background with this shouldComponentUpdate implementation:
shouldComponentUpdate(nextProps) {
if (!_.empty(this.props.restrictRerendersToRoutes)) {
return !!this.props.restrictRerendersToRoutes
.find((routeKey) => routeKey === nextProps.currentScene.name);
}
return true;
}

The problem is indeed that the whole navigation stack is connected to the store( because components are not unmounted unless you use resetto or resetnavigation )
For what it's worth here is what I do right now.
I have been working with a slightly modified react redux implementation that skips updates for scenes that are not in view ( see below )
To use it you need to:
Store the route for one component tree in context
We created a wrapper for this
const CurrentRoute = React.createClass({
childContextTypes : { currentRoute: PropTypes.object },
getChildContext() {
return { currentRoute: this.props.route }
},
render() { return this.props.children; }
})
And use it in render scene
<CurrentRoute route={route}><CurrentScene navigate={navigate} route={route} index={index} /></CurrentRoute>
Then you can access the route a component has been rendered into.
Store the navigation stack in a singleton
We use this code in configure scene
let routeStack = [];
export const updateRouteStack = stack => routeStack = stack;
Then you can use this slightly modified react-redux connect function, it will skip updates if component is rendered in another component tree then the currently displayed one
( or a similar implementation )
https://github.com/reactjs/react-redux/compare/master...ganmor:master
It might be possible to package this but I haven't had the time to look into it. If anyone feels like doing it..
Hope that helps

Related

Laravel + InertiaJS + Vue 3: Play transition on page BEFORE navigation with Link, button, etc

TL;DR: In a Laravel + InertiaJS + Vue 3 developed application, how can I achieve to have a transition between "pages" loaded within a persistent layout <main> section - for example, have that section animate (say, fade out) before loading the next page, then animate (fade) that new page in - when using standard Inertia routing for navigation? I have managed to do it on entering/showing the page, but have found no way to animate before navigation happens.
LONG(ish): The Way I'm trying to do it
Let's assume there is an application (developed with Laravel + InertiaJS + Vue 3).
I have an element in the markup of an Inertia persistent layout that is conditionally shown if a value is true (v-if="shouldAnimate") that is initially set to false when declared, and when onMounted fires, it sets that value to true which in turn triggers the animation to run (doesn't really matter how the animation works, but just in case, I have options to use either GSAP or anime.js).
Up to this point, all is good: every time I navigate to a page (using Inertia-adequate methods such as the Link component) the animation triggers and I am a happy guy.
BUT: I would very much like to be able to play another animation (the reverse of the previous one) before, say, navigation to the next page occurs. I have tried almost everything I can think of and have not been successful. Here's what got the closest to what I need:
I tried hooking into the InertiaJS event Inertia.on('before', ...): effectively, the event fires up right before navigation (checked with some good-old console log), so I tried firing up the animation at this point, only to find out that the Inertia event looks like it is destroying the page immediately before the animation has had time to play; no problem: I'll just event.preventDefault() it, run the animation and THEN, using a setTimeout timed to the length of the animation (300ms) I'll resume navigation, say, by using Inertia.visit.
Doesn't work. Somehow, the default behaviour is prevented (stops the navigation), the animation plays back, but when it comes to the "resume navigation part" I have had mixed results depending on what I use:
Code looks roughly like this:
let removeListener = Inertia.on('before', (event) => {
event.preventDefault()
// Play animation here
setTimeout(() => {
// SOME INERTIA ACTION DESCRIBED BELOW
}, 300)
})
Independently of whether I use Inertia.get(event.detail.visit.url) or Inertia.visit(event.detail.visit.url) what happens is the animation runs its course, and then the timer runs out and this whole code RUNS AGAIN AND AGAIN in intervals equal to the timer. I also tried to do this using the complete event of the animation to trigger the navigation but it behaves the same.
I know this is related to me being an ignorant about how both Inertia and events work, and I am sure there is a proper (correct? right?) way to achieve what I need, but either I have failed in using the correct terms to look for it, or I am approaching this the wrong way. Hopefully this information is enough to explain my issue.
Any help or pointer would be GREATLY appreciated, so thanks in advance.

Unity UI not working properly ONLY on Windows

I have been working on figuring out what is going on with my game's UI for at least two days now, and no progress.
Note that this is a mobile game, but I was asked to build for Windows for visualization and presentation purpose.
So the problem is that when I run my game on the Unity Editor, Android, iOS and Mac platforms the UI works just perfect, but then when I run the game on Windows the UI still works fine UNTIL I load a specific scene.
This specific scene is a loading screen (between main menu and a level) when the level finished async loading, a method called MoveObjects is called in a script in the loading screen, to move some objects that where spawned in the loading screen scene into the level scene (this is not the issue though, since I already try without this method and the problem on the UI persist).
Once the logic of this MoveObjects method is done, a start button is enabled in the loading screen, for the player to click and start playing (I did try moving the start button to the level scene, since maybe it not been a child of the currently active scene could be the issue, but the problem still persist). Is at this point that the UI is partially broken, what I mean with this is, that I can see buttons (and some other UI elements like a scrollbar) changing color/state when the mouse moves over them, but I cannot click on them anymore (the button wont even change to the pressed state).
Also note that I tried creating a development build to see if there was any errors in the console, and I notice that this problem is also affecting the old UI system, so I was not able to interact with the development console anymore.
Also also, note that if I grab and drag the scrollbar before this issue appear, and I keep holding down on the scrollbar until this happens, the mouse gets stuck on the scrollbar, meaning that I cannot interact with the UI anymore, but the scrollbar will still move with the mouse.
I already check that this things are not the source of the problem:
Missing EventSystem, GraphicRaycaster or InputModule.
Another UI element blocking the rest of the UI.
Canvas is Screen Space - Overlay so there is no need for a camera reference.
I only have one EventSystem.
Time.timeScale is 1.
I am not sure what else I could try, so if anyone has any suggestions, I would appreciate it. Thanks.
P.S: I am sorry to say that I cannot share any code or visual material or examples due to the confidentiality.
A major source for a non-working UI for me has always been another (invisible) UI object blocking the raycast (a transparent Image, or a large Text object with raycast on).
Here's a snippet I put together based on info found elsewhere, I often use it to track objects that are masking the raycast in complex UI situations. Place the component on a text object, make sure it's at least few lines tall, as the results will be displayed one under another.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
[RequireComponent(typeof(Text))]
public class DebugShowUnderCursor : MonoBehaviour
{
Text text;
EventSystem eventSystem;
List<RaycastResult> list;
void Start()
{
eventSystem = EventSystem.current;
text = GetComponent<Text>();
text.raycastTarget=false;
}
public List<RaycastResult> RaycastMouse(){
PointerEventData pointerData = new PointerEventData (EventSystem.current) { pointerId = -1, };
pointerData.position = Input.mousePosition;
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(pointerData, results);
return results;
}
void Update()
{
list= RaycastMouse();
string objects="";
foreach ( RaycastResult result in list)
objects+=result.gameObject.name+"\n";
text.text = objects;
}
}

How to go back more than one view model in mvvm light

I am doing xamarin development and I am not doing forms.
I want to go back by 3 viewcontrollers/activities but goback method is for going back by one viewcontroller or activity
If I use navigateto i believe one more time the viewcontrolller/activity gets added.i.e two instances.
So how to solve this problem ?
Update:
Here is the inavigation interface there is no way to access thr navigational stack as well
If you are going back, then use INavigationService.GoBack();. You can call that 3 times in a row to go back 3 pages.
private void GoBackThree()
{
_navigationService.GoBack();
_navigationService.GoBack();
_navigationService.GoBack();
}
I haven't used MVVM Light before but I have quite a bit of experience of Xamarin Forms. I used to use MVVM Cross for navigation but eventually found that the built in Xamarin Forms Navigation was much better, I ran into similar scenarios as to what you have here.
Under the hood it looks like MVVM Light is just wrapping the Xamarin Forms Navigation anyway - http://mvvmlight.codeplex.com/SourceControl/latest#Samples/Flowers/Flowers.Forms/Flowers.Forms/Helpers/NavigationService.cs
I don't think you need give up on MVVM Light navigation but you do need to get under the hood to achieve the navigation without seeing three transitions. I haven't tried this with MVVM Light but it works great with normal Forms navigation.
Step 1
Get access to the underlying Xamarin Forms navigation:
var navigation = Application.Current.MainPage.Navigation;
Step 2
Remove the two pages you want to skip when navigating back. It's important that you remove them before navigating backwards otherwise you'll get a double transition (note the -2 is because you want to remove the second to last page).
var firstPageToRemove = navigation.NavigationStack[navigation.NavigationStack.Count - 2];
navigation.RemovePage(firstPageToRemove);
var secondPageToRemove = navigation.NavigationStack[navigation.NavigationStack.Count - 2];
navigation.RemovePage(secondPageToRemove);
Step 3
Navigate backwards
_navigationService.GoBack();
I hope that works for you.
Alternative Consideration
I have a similar application in my app which I've solved a little more elegantly. If you know you never need to be able to navigate back to those previous pages. When you push the new pages on, you can actually remove the previous one if you no longer need it. Please note you have to remove the page after you push on the new one otherwise you get two transitions. I use this extension method on-top of the standard Forms Navigation which I showed you how to access in Step 1 to achieve it.
public static async Task ReplaceCurrentAsync(this INavigation navigation, Page page, bool animated = false)
{
var curentPage = navigation.NavigationStack.Last();
await navigation.PushAsync(page, animated);
navigation.RemovePage(curentPage);
}

React Native - Adding a new view seems to have a default fade in animation

Every time I render a new view it seems to have a default fade in animation where the opacity goes from 0 - 1 over about 200 milliseconds. Is there any way to turn this off so the view appears immediately?
Sample code that would trigger the default animation;
{(() => {
if (itemIsOpen) {
return (
<CardOverlay />
)
}
})()}
CardOverlay component fades in when I want it to immediately appear.
I've been struggling with the same thing for a while now, it's been driving me around the bend but I've finally found the cause of it. Not sure if it's the same thing that caused your problem (and not sure if it's still relevant for you) but I thought I'd share it incase someone runs into the same problem.
My issue was caused by Animated.spring() -- I was using it on a sub-component (a little button that makes an image appear on the page) but it seems that as soon as you add spring anywhere in your visual tree it affects everything. Even if you put it in componentDidMount on the inner component it still affects the page-load animation for me.
Time to learn a bit more about how the react Animated library works methinks!

What is the proper way to change scenes?

Back when I did LUA i used to run dofile("..."); to load other lua files and such. Later to find out that this is a very bad practice and can lead to applications breaks.
Now that I am on my way to developing a WebOs application, i want to make sure I am properly changing scene before i pick up bad programming habit.
At the moment this is what I use:
label2Tap: function(inSender, event) {
Mojo.Controller.stageController.popScene();
Mojo.Controller.stageController.swapScene("LogicAndArithmetic");
},
Which works great to get to my LogicAndArithmetic scene, is this the best practice to do such?
Thanks.
The scene model in the Mojo framework of webOS works like a stack. When the app starts you call pushScene to display your main scene. Normally, you then make additional pushScene calls to add scenes to the stack on top, and then when you are done with them they are popped, typically when the user performs the 'back gesture', bringing back the previous scene. Eventually you will be back at your main scene.
The swapScene call is equivalent to calling popScene and then pushScene for a different scene. In your case you are calling popScene then swapScene, so that is the equivalent of popping two scenes from the stack and then pushing back one. It probably works because you have only one scene, but if you had more it would not work correctly.
BTW, why are you working with Mojo and not Enyo?
While this is technically correct, it is most likely not how the user would expect your app to behave. In general, when a user presses a button that opens a new scene, it is placed on the stack, as Miguel said. The user will expect to be able to go back and pop the scene off of the stack. This happens automatically, you do not need to listen for this input. You do this by calling Mojo.Controller.pushScene("sceneName");. While there are some applications where swapScene makes sense, your app can probably be conceptualized as a stack of scenes, with a logical "back" scene.
I would reccommend playing around with some existing apps to get a feel for how they behave. Also, while Miguel suggested moving to Enyo, it is worth noting that Enyo apps are not officially supposed to work on webOS 2 devices (phones), only on the touchpad. It is possible to run them on webOS 2 devices, but I do believe that they will be rejected from the app catalog.

Resources