Error in navigation after creating new Realm object - android

I'm using the react-native-navigation package as well as Realm in my project. My app has a realm object called Notebook that contains a list of Verse objects, which are another realm object.
The structure of the app is very simple, the first page shows a list of Notebooks and when one is selected, the app transitions to the second screen which is a list of Verses.
Here is my code to navigate from the notebook list to the verse list:
this.props.navigator.push({
screen: 'com.beyersapps.biblebinderrn.verselist',
title: notebook.name,
passProps: {notebook: notebook},
animated: true,
animationType: 'fade',
backButtonTitle: undefined,
backButtonHidden: false
})
This navigation works fine and I can move back and forth between the two screens. My problem comes when I create a new Verse and add it to the Notebook. Here is my code that lives in the second screen to create a new Verse:
realm.write(() => {
let newVerse = realm.create('Verse', {
reference: 'Genesis 1:1',
favorite: false,
memorized: false,
scripture: 'My favorite verse'
});
if (this.notebook != null) {
this.notebook.verses.push(newVerse);
}
});
This is where my problem starts. At this point, if I select the back button to go back to the list of Notebooks, then select a notebook again, I get this error:
Attempting to change value of a readonly property.
Exception in native call
java.lang.RuntimeException: Error calling RCTEventEmitter.receiveTouches
at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31)
at android.os.Looper.loop(Looper.java:154)
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:199)
at java.lang.Thread.run(Thread.java:761)
Caused by: com.facebook.jni.CppException: Exception calling object as function: TypeError: Attempting to change value of a readonly property.
at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method) 
at android.os.Handler.handleCallback(Handler.java:751) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31) 
at android.os.Looper.loop(Looper.java:154) 
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:199) 
at java.lang.Thread.run(Thread.java:761) 
There are a couple of things I can do to make this problem go away, but both make my app useless. Since they might help in determining the issue though I can either remove {notebook: notebook} from passProps when I navigate to the new screen (but then nothing shows on the Verse list screen since it does not know which Notebook was selected). Or, I can not add the newly created Verse to the selected Notebook (but then I can't add data).
Since these two changes are in two different components (Realm and react-native-navigation), I'm not sure which component is the source of the problem.

Objects passed as props (passProps) are frozen by React Native.
Try changing:
passProps: {notebook: notebook}
to:
passProps: {notebook: _.cloneDeep(notebook)}
If you need to reflect changes to the notebook in the previous screen, I think you should do so through the store.
Another option is to pass notebookId and get the correct notebook by id from state.

Related

Android Compose AdapterList update data Asynchronously

Hi I'm trying to update the AdapterList Composable item asynchronously. I put Image as one of the List Item. The image data is downloading from server using coroutine and update the value using state. When I fling the list items, got following error
java.lang.IllegalStateException: Asking for measurement result of unmeasured layout modifier
at androidx.ui.core.LayoutNodeWrapper.getMeasureResult(LayoutNodeWrapper.kt:58)
at androidx.ui.core.LayoutNodeWrapper.getMeasuredSize(LayoutNodeWrapper.kt:48)
at androidx.ui.core.Placeable.getWidth(Placeable.kt:40)
at androidx.ui.core.LayoutNode.getWidth(ComponentNodes.kt:841)
at androidx.ui.foundation.ListState.composeAndMeasureNextItem-BTEqjtU(AdapterList.kt:222)
at androidx.ui.foundation.ListState.consumePendingScroll(AdapterList.kt:151)
at androidx.ui.foundation.ListState.access$consumePendingScroll$3(Unknown Source:0)
at androidx.ui.foundation.ListState$ListMeasureBlocks.measure(AdapterList.kt:277)
at androidx.ui.core.InnerPlaceable.performMeasure(InnerPlaceable.kt:43)
at androidx.ui.core.LayoutNodeWrapper.measure(LayoutNodeWrapper.kt:99)
at androidx.ui.core.DelegatingLayoutNodeWrapper.performMeasure(DelegatingLayoutNodeWrapper.kt:79)
at androidx.ui.core.LayerWrapper.performMeasure(LayerWrapper.kt:52)
at androidx.ui.core.LayoutNodeWrapper.measure(LayoutNodeWrapper.kt:99)
at androidx.ui.core.DelegatingLayoutNodeWrapper.performMeasure(DelegatingLayoutNodeWrapper.kt:79)
at androidx.ui.core.LayoutNodeWrapper.measure(LayoutNodeWrapper.kt:99)
at androidx.ui.core.DelegatingLayoutNodeWrapper.performMeasure(DelegatingLayoutNodeWrapper.kt:79)
at androidx.ui.core.LayoutNodeWrapper.measure(LayoutNodeWrapper.kt:99)
at androidx.ui.core.DelegatingLayoutNodeWrapper.performMeasure(DelegatingLayoutNodeWrapper.kt:79)
at androidx.ui.core.LayoutNodeWrapper.measure(LayoutNodeWrapper.kt:99)
at androidx.ui.core.LayoutNode$measure$2.invoke(ComponentNodes.kt:1177)
at androidx.ui.core.LayoutNode$measure$2.invoke(Unknown Source:0)
at androidx.ui.core.ModelObserver.observeReads(ModelObserver.kt:151)
at androidx.ui.core.AndroidComposeView.observeMeasureModelReads(AndroidOwner.kt:487)
at androidx.ui.core.LayoutNode.measure(ComponentNodes.kt:1176)
I've seen this a few times myself with both lists downloading images like you're describing and also lists without any async work being done, but I don't think it's caused by anything we're specifically doing. My impression is that it's just a bug with the current state of Compose.
That being said, AndroidComposeViewAccessibilityDelegateCompat is at least one class that handles this error and references an internal Issue Tracker ticket that's indicating it will be fixed in Android R, at least for that instance.
} catch (e: IllegalStateException) {
// We may get "Asking for measurement result of unmeasured layout modifier" error.
// TODO(b/153198816): check whether we still get this exception when R is in.
info.setBoundsInScreen(android.graphics.Rect())
}
There's also an upcoming change in dev11 that updates AdapterList to dispose of compositions scrolled off screen and I'm curious to see how this affects things.
And if you're just curious about where the error is being thrown you can check out LayoutNodeWrapper._measureResult.
This issue is fixed in Compose version 0.1.0-dev14
You can update your compose dependencies to
composeOptions {
kotlinCompilerExtensionVersion '0.1.0-dev14'
kotlinCompilerVersion '1.3.70-dev-withExperimentalGoogleExtensions-20200424'
}

React Native: How to update the view which depends on nework responses

Suppose I have two screens: HomeScreen & LocationScreen
Steps: Firstly I navigated from HomeScreen(state={location: 'A'}) to LocationScreen.
Changed location in LocationScreen(state={location: 'B'})
Pass it to HomeScreen and change location from A to B.
Now , HomeScreen has View dependent on location state.
So my question is , how can I update the view content of home screen as the content is coming from network response after the location been updated?
What you are suffering from is a common problem of state management in React. For easing your trouble, there is another library called Redux. Give it a read.
To answer your question, Redux provides a connect function which has two arguments : 1) mapStateToProps & 2) mapDispatchToProps. You can easily solve your problem with these two functions and a lifecycle method called "componentWillReceiveProps(nextProps)".
Here is an example you can refer to : https://stackoverflow.com/a/38203735/2164029
Redux is a great tool for app state management, especially for the state that is shared in a few places in the app.
In your use case though, I think the regular React state should be sufficient.
After you pass the changed location from Location screen back to Home screen, Home screen can in term trigger the fetching for data. By storing the content on the component state, you can easily refer to them in render function. this.setState triggers re-render of the component, as any state change or prop change will cause a re-render
e.g.
class HomeScreen extends Component {
...
onLocationChange(newLocation) {
this.setState({ loading: true, newLocation }); // Loading data, and storing new location
fetchDataBasedOnLocation().then((data) => {
this.setState({ content: data. loading: false });
});
}
render() {
return (
...
{Your content in this.state.content}
...
);
}
}
Hope this is helpful!

Xamarin.Forms Adding Children Pages to TabbedPage

My Main Page in my App is a TabbedPage
I have three Tabs:
Payment, Config, Maintain
One of the tabs is controlled by a setting to show it or not. When that is changed to false I remove the tab by doing the following:
tabPage.Children.Remove(ConfigTab);
This removal works fine. If I turn the tab back on I can Add the tab by using this code:
tabPage.Children.Add(ConfigTab);
But its added at the end of the list and the Navigation header is missing:
I looked at using the Insert method where I can sepcify an index
tabPage.Children.Insert(1,ConfigTab);
but this crashed the app with the following message:
Unhandled Exception: Java.Lang.IndexOutOfBoundsException: Invalid
index 2, size is 2
If I inspect the children at that point it has added the page at the correct index
Any suggestions on how I can dynamically insert a new page to a TabbedPage? And retain the navigation?
UPDATE:
I have now managed to get it working by doing the following :
var paymentPage = tabPage.Children.FirstOrDefault(p=> p.ClassId == "PaymentNavPage");
var configPage = GetConfigPage();
var maintenancePage = tabPage.Children.FirstOrDefault(p => p.ClassId == "MaintaintNavPage");
// Clear old Tabgs
tabPage.Children.Clear();
// Put pages back
tabPage.Children.Add(paymentPage);
tabPage.Children.Add(configPage);
tabPage.Children.Add(maintenancePage);
I'm still interested if there is a better way to use the Insert method and then to reset the Navigation Stack.
I'm experiencing the same thing with Insert to the Children. Your solution seems to be the only option to hide and show a page. I would suggest assigning the pages to variables so you don't have to loop through the Children every time you are hiding/showing ConfigPage. Based on the names I'm assuming that the pages are different from each other.
In the case of similar pages ItemsSource can be used with the help of DataTemplate. When binding the ItemsSource to ObservableCollection hiding and showing pages requires just to modify that collection. More info about that can be found here:
https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/navigation/tabbed-page/#Populating_a_TabbedPage_with_a_Template
The next code work for me:
tabPage.Children.Insert(1,ConfigTab);

React Native: Change Component (Text) on Navigator Pop

I'm creating a Component/View that has a list of things to select from similar to a table view.
When I'm in View A I can jump to this table view by doing a navigator push. Once an item is selected I do a pop. However, I'd like a text component in View A to be updated with the appropriate value.
I was thinking of passing a reference to this text component but it doesn't sound right. Any other ways I could achieve that?
I couldn't find any table view that would work on both platforms, let me know if you have any good suggestions.
The React way to achieve this is to pass a callback function to a named attribute, let's say in this case onSelect.
MyParentComponent extends Component {
...
MySelectHandler(value) {
this.setState({
valueSelected: value
});
},
...
}
Creating your picker component:
<MyCustomTableView onSelect={this.MySelectHandler}/>
And in your component:
MyCustomTableView extends Component {
...
onValueSelected(value) {
this.props.onSelect(value);
}
...
}
Considering your "re-render" problem, React only updates Component who have changed depending on the previous state. As your inputs probably don't depend on the state, they don't get updated because nothing changed for them.
If you want to clear all your inputs when the valueSelected key state gets updated, you can use the lifecycle method componentDidUpdate(), and manually clear your inputs after each value update.
// In MyParentComponent
componentDidUpdate(prevProps, prevState) {
if (prevState.valueSelected !== this.state.valueSelected)
this.refs.myInput.value = ''; // Do this for each input, you'll need to add a unique ref attribute for each one
}
...
I solved it by passing {this} to the table view as a prop and calling this.props.parent.setState({selection: selectedThing}) in the table view.
I thought setState would re-render the whole view and get rid of any user inputs but it seems to only re-render changed components

Crash in Activity Transitions with SharedElement

I'm using Activity transitions from a ViewPager (in the calling activity) with a shared element and content transitions as well. I'm getting this crash when re-entering to the calling activity:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.os.ResultReceiver.send(int, android.os.Bundle)' on a null object reference
at android.app.EnterTransitionCoordinator.sendSharedElementDestination(EnterTransitionCoordinator.java:199)
at android.app.EnterTransitionCoordinator.viewsReady(EnterTransitionCoordinator.java:123)
at android.app.EnterTransitionCoordinator$2.onPreDraw(EnterTransitionCoordinator.java:148)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:895)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2153)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1180)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6558)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777)
at android.view.Choreographer.doCallbacks(Choreographer.java:590)
at android.view.Choreographer.doFrame(Choreographer.java:560)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:763)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5832)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
Also, Once going back, the screen begins to flicker continuously with a white screen flashing in and out.
Here are my Transition flags:
<item name="android:windowContentTransitions">true</item>
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowAllowReturnTransitionOverlap">false</item>
I tried setting Enter/Exit transitions both on the Calling and Called activity but no luck.
Try to get fragment from FragmentManager:
fragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + position)
If fragment is null, try creating a new fragment.
Activity transitions with shared elements can sometimes result in crashes due to various reasons. Here are a few common causes of crashes and how to avoid them:
Timing issues: Make sure that the shared elements have been properly sized and laid out before the transition begins. Delaying the transition start until after the shared elements have been fully initialized can prevent crashes.
Inconsistent names: If you are using shared element transitions between activities, make sure that the names of the shared elements in both activities are the same. Mismatched names can result in a crash.
Improper use of View.setTransitionName(): When using shared element transitions, it's important to set a unique transition name for each shared element. If two shared elements have the same transition name, a crash can occur.
OutOfMemoryErrors: Large images or Bitmaps used as shared elements can cause OutOfMemoryErrors, resulting in a crash. To avoid this, make sure to resize the images or Bitmaps to a smaller size before using them as shared elements.
Missing transition in XML: If the transition between the two activities is not defined in the XML, a crash can occur. Make sure that the transition is properly defined in the XML and that the correct transition is being used for the shared elements.
Here's an best example of avoid timing issues when using shared elements:
private void startActivityWithSharedElement(Intent intent) {
final ImageView sharedImageView = findViewById(R.id.shared_image_view);
sharedImageView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
sharedImageView.getViewTreeObserver().removeOnPreDrawListener(this);
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(
MainActivity.this, sharedImageView, "shared_image").toBundle());
return true;
}
});
}

Categories

Resources