Cannot modify managed objects outside of a write transaction - React Native, Realm - react-native

onPressFavourites(item) {
let realm = Realm.open({
path: RNFS.DocumentDirectoryPath +'/trees.realm',
schema: [sightingSchema, treeSchema],
schemaVersion: 7,
}).then(realm => {
if(item.favourites === 0) {
realm.write(() => {
item.favourites = 1;
item.favouritesColour = '#91b54d';
});
} else {
realm.write(() => {
item.favourites = 0;
item.favouritesColour = 'transparent';
});
}
});
alert(item.favourites)// Update a property value
}
I am trying to update an object in the Realm when a button is clicked however I get the error
"Possible Unhandled Promise Rejection (id: 0):
Error: Cannot modify managed objects outside of a write transaction."
This code was working a couple days ago but is now throwing the above error.
I am still learning React Native and Realm but from my understanding and following examples and the Realm docs, I am using the correct code so it should work.
EDIT
Seems the realm and write transactions were fine.
We were able to find a roundabout way to fix it however now the updates don't display until the app is refreshed.
It seems the error Possible Unhandled Promise Rejection (id: 0):
Error: Cannot modify managed objects outside of a write transaction. was produced when using the argument item to define the chosen Realm Object. However, if we create a variable related to the item, it doesn't produce that error. Any ideas why this would happen?
onPressFavourites(item) {
//console.log(realm);
let realm = Realm.open({
path: RNFS.DocumentDirectoryPath +'/trees.realm',
schema: [sightingSchema, treeSchema],
schemaVersion: 7,
}).then(realm => {
let trees = realm.objects('TreeInfo');
for(var i=0; i<trees.length;i++){
if(trees[i].commonName == item.commonName){
var chosen = trees[i];
break;
}
}
console.log(chosen);
if(item.favourites === 0) {
realm.write(() => {
//item.favourites = 1;
//item.favouritesColour = '#91b54d';
chosen.favourites = 1;
chosen.favouritesColour = '#91b54d';
});
}
else {
realm.write(() => {
chosen.favourites = 0;
chosen.favouritesColour = 'transparent';
});
}
});
alert(item.favourites)// Update a property value
}
EDIT
render() {
var treeList = this.state.trees;
console.log(treeList);
const {navigate} = this.props.navigation;
//var tree = treeList[0];
return(
<Container>
<Header searchBar style={styles.searchBar}>
<Item style={styles.searchBarInner}>
<Icon name="ios-search" />
<Input placeholder="Search" />
</Item>
</Header>
<List dataArray={treeList}
renderRow={(item) =>
<ListItem style={styles.ListItem} button={true}>
<ListButton item={item}
onSelect={(item) => this.saveTreeInfo(item)}
onSelectFavourites={(item) => this.onPressFavourites(item)
}
/>
</ListItem>
}
>
</List>
</Container>
);
}
Above is where item is being passed to the onPressFavourites function. item is being generated from an array of Realm Objects treelist and displayed in a list.
The treelist array comes from the variable this.state.trees which is displayed below.
filterContent(){
Realm.open({
path: RNFS.DocumentDirectoryPath +'/trees.realm',
schema: [sightingSchema, treeSchema],
schemaVersion: 7,
}).then(realm => {
let trees = realm.objects('TreeInfo');
let length = trees.length;
let treeRealm = trees.sorted('commonName');
console.log(this.state.leafEdges);
if (this.state.leafEdges === 'smooth') {
var smoothLeaf = treeRealm.filtered('leafEdges CONTAINS "Smooth"');
this.setState({trees:smoothLeaf});
}
if (this.state.leafEdges === 'toothed') {
var toothedLeaf = treeRealm.filtered('leafEdges CONTAINS "Toothed"');
this.setState({trees:toothedLeaf});
}
if (this.state.leafEdges === 'notsure') {
this.setState({trees:treeRealm});
}
else if (this.state.leafEdges === 'null') {
this.setState({trees:treeRealm});
}
});
}

Answer in English:
The trees has something about realm, so you cannot change it. Just copy a new Array like this:
let trees = realm.objects('TreeInfo');
var arr=[];
for (let i = 0;trees && i < trees .length; i++) {
var item=trees[i];
var item2={
id: item.id,
....
}
arr.push(item2);
}

You should improve the syntax
let order = {...bla, bla,bla}
realm.write(() => {
realm.create("order", order);
});

这个trees 里面关联了realm里的一些东西,所以不能改变,要重新复制一个数组,像这样
let trees = realm.objects('TreeInfo');
var arr=[];
for (let i = 0;trees && i < trees .length; i++) {
var item=trees[i];
var item2={
id: item.id,
....
}
arr.push(item2);
}

Related

Cant receive data from postMessage in react native webview

here is my injected js:
window.ReactNativeWebView.postMessage(JSON.stringify({
external_url_open: null,
height: Math.max(document.body.offsetHeight, document.body.scrollHeight, document.documentElement.clientHeight)
}));
(function(){
var attachEvent = function(elem, event, callback)
{
...
}
var all_links = document.querySelectorAll('a[href]');
if ( all_links ) {
for ( var i in all_links ) {
if ( all_links.hasOwnProperty(i) ) {
attachEvent(all_links[i], 'onclick', function(e){
if ( new RegExp( '^https?:\/\/www.dropbox\.com.*', 'gi' ).test( this.href ) ) {
// handle external URL
e.preventDefault();
window.ReactNativeWebView.postMessage(JSON.stringify({
external_url_open: this.href,
}));
}
else { window.location.href = this.href; }
});
}
}
}
})();
I want to receive the pages height on react native side with this code:
<WebView
...
javascriptEnabled={true}
onMessage={(event) => {
// retrieve event data
var data = event.nativeEvent.data; // maybe parse stringified JSON
try {
data = JSON.parse(data)
} catch ( e ) { }
// check if this message concerns us
if ( 'object' == typeof data && data.external_url_open ) {
if (data.external_url_open != null){
// proceed with URL open request
Linking.openURL(data.external_url_open);
}
else {
height = data.height;
console.log(data.page_height);
}
}
}}
injectedJavaScript={jsCode}
...
/>
the link handling part works but the height receive part doesn't and I dont see anything in console when refreshing the page. Can anyone help?
I just needed to relocate this else to the outer if:
else {
height = data.height;
console.log(data.page_height);
}

Vue Filter get back elements from an subobject

I have an object that looks like this:
{
"itemsTasks":[
{
"id":1,
"files":[
]
},
{
"id":2,
"files":[
]
}
]
}
Now I want to filter and get only back the files object from the object with a special id:
this.editedIndex = 1
this.editedItem.files = this.itemsTasks.filter((obj) => {
return obj.id === this.editedIndex;
})
But with this I get back the hole object. How can I now get back only the files part?
EDIT
With the help of #Shamsail I have found the solution
this.editedItem.files = getFilesFromItemsTasks(this.editedIndex, this.itemsTasks)
function getFilesFromItemsTasks(index, tasks) {
let result = tasks.filter((obj) => {
return obj.id === index
})
return result[0].files
}
may be you can use a function for it
this.editedIndex = 1
this.editedItem.files = getFilesFromItemsTasks();
getFilesFromItemsTasks(){
let result = this.itemsTasks.filter((obj) => {
return obj.id === this.editedIndex;
})
return result.files
}

Issue with react-native FlatList rendering data when updating array of objects maintained in redux

I'm loading a dynamic form in a FlatList. I'm supposed to add few rows with UI elements such as TextInput, checkbox, etc. based on a condition. For example, I have a dropdown, when I select a value, I have to add a row with a button and remove that row when I select another value.
My issue is that whenever I select a value in the dropdown to add a row with button, FlatList is adding duplicate rows and they are not visible as objects in my array when I debug them. I'm using Redux to manage my application state and updating my array in reducers.
I have to display objects in my array in FlatList without any duplicates using Redux for state management.
Code:
<FlatList
key={Math.floor(Math.random())}
ref={(ref) => { this.flatListRef = ref; }}
data={this.state.mainArray}
renderItem={this._renderItem}
keyExtractor={({ item, id }) => String(id)}
extraData={this.prop}
keyboardDismissMode="on-drag"
showsVerticalScrollIndicator={false}
scrollEnabled={true}
removeClippedSubviews={false}
scrollsToTop={false}
initialNumToRender={100}
/>
componentWillReceiveProps(nextProps) {
this.setState({mainArray: nextProps.mainArray});
}
Updating array in Reducer:
case VALIDATE_DEPENDENCY: {
// adding or removing dependency questions
let dependingQues = [];
let formArrayTemp = [...state.mainArray];
let exists;
// console.log("formArrayTemp123", formArrayTemp);
if (action.item.isDependentQuestion) {
if (action.item.dependentQuestionsArray.length > 0) {
let dependent_ques = action.item.dependentQuestionsArray[0];
state.depending_questions_array.filter((obj, id) => {
if (JSON.stringify(obj.key_array) === JSON.stringify(dependent_ques.key)) {
dependingQues.push(obj);
}
});
// console.log("dependingQues123", dependingQues);
if (dependent_ques.answer === action.item.answer) {
dependingQues.map((obj, id) => {
exists = formArrayTemp.filter((res, idx) => {
if (JSON.stringify(res.key_array) === JSON.stringify(obj.key_array)) {
return res;
}
});
// console.log("exists123", exists);
if (exists.length === 0) {
formArrayTemp.splice(action.index + id + 1, 0, obj);
// console.log("mjkgthbythyhy", action.index + id + 1);
}
});
}
else {
let objIndex = formArrayTemp.findIndex(obj => JSON.stringify(obj.key_array) === JSON.stringify(dependent_ques.key));
if (objIndex !== -1) {
formArrayTemp[objIndex].answer = "";
formArrayTemp[objIndex].borderColor = "black";
formArrayTemp.splice(objIndex, 1);
// console.log("frtg6hyjukiu", formArrayTemp);
}
}
}
}
// adding or removing dependent sections
let dependentSections = [];
let tempArr = [];
let count = 0;
if (action.item.isDependent) {
if (action.item.dependentArray.length > 0) {
let dependent_section = action.item.dependentArray[0];
state.dependent_sections_array.filter((obj, id) => {
if (obj.section_id === dependent_section.section_id) {
dependentSections.push(obj);
}
});
// console.log("dependentSections123", dependentSections);
let lastIndex;
formArrayTemp.filter((res, id) => {
if (action.item.section_id === res.section_id && res.dependentArray &&
JSON.stringify(action.item.dependentArray) === JSON.stringify(res.dependentArray)) {
tempArr.push(res);
lastIndex = formArrayTemp.FindLastIndex(a => a.section_id === res.section_id);
}
});
// console.log("lastIndex123", lastIndex);
tempArr.map((obj, id) => {
if (obj.answer === obj.dependentArray[0].answer) {
count++;
}
});
if (dependent_section.answer === action.item.answer) {
dependentSections.map((obj, id) => {
if (formArrayTemp.contains(obj) === false) {
formArrayTemp.splice(lastIndex + id + 1, 0, obj);
// console.log("cfrererfre", formArrayTemp);
}
});
}
else {
let countTemp = [];
if (count === 0) {
let countTemp = [];
dependentSections.filter((res, id) => {
if (formArrayTemp.contains(res) === true) {
formArrayTemp.filter((obj, idx) => {
if (obj.section_id === res.section_id) {
obj.answer = "";
obj.borderColor = "black";
countTemp.push(obj);
}
});
}
});
let jobsUnique = Array.from(new Set(countTemp));
formArrayTemp = formArrayTemp.filter(item => !jobsUnique.includes(item));
// console.log("frefrefre123", formArrayTemp);
// return { ...state, mainArray: [...formArrayTemp] }
}
}
}
}
console.log("formArray123", formArrayTemp);
formArrayTemp.sort((a, b) => {
return a.posiiton - b.position;
});
return { ...state, mainArray: formArrayTemp }
};
Update
After researching this issue I came across this issue #24206 in GitHub. There it states:
There's a few things you aren't doing quite correctly here. Each time the render function runs you are creating a new array object for your data, so React has to rerender the entire list each time, you're renderItem function and keyExtractor function are also remade each rerender, and so the FlatList has to be rerun. You're also using the index for your key which means as soon as anything moves it will rerender everything after that point. I would recommend looking at what does/doesn't change when you rerun things (arrays, functions, object), and why the keyExtractor is important for reducing re-renders.
So I tried commenting KeyExtractor prop of FlatList and the issue got resolved. But this is causing issue in another form. How can I resolve that?

Primeng loading icon is not updating

I am using primeng turbo table with lazy loading and enabled loading to attribute it's not getting updated even got updated
<p-table #dt [loading]="loader" [columns]="cols" [value]="datasource.merchants" [rows]="perPage" [paginator]="true" [pageLinks]="5"
[lazy]="true" [totalRecords]="datasource.totalCount" (onLazyLoad)="loadLazyData($event)" [exportFilename]="'merchant-list'">
.....
Script
loadLazyData(event: LazyLoadEvent) {
if (event.first !== this.searchParams.offset || event.rows !== this.searchParams.limit) {
this.loader = true;
this.searchParams.offset = event.first;
this.searchParams.limit = event.rows;
this.apiService.getResponse(this.smartSearchParams.query, event.first, event.rows)
.subscribe((result) => {
this.datasource = result;
let newArray = result.merchants.slice();
this.dataTable.value = newArray;
setTimeout(() => {
this.loader = false;
this.dataTable.loading = this.loader;
});
}, (err: any) => {
setTimeout(() => {
this.loader = false;
this.dataTable.loading = this.loader;
});
});
}
can someone help me with this issue
The issue is with changeDetection for the component.
I have removed the changeDetection:ChangeDetectionStrategy.OnPush from my component its started working

Render the component only after getting data from server

I have to show options menu using conditional check on data coming from the server. Here is my code:
function renderMoreOptionsOnNavbar(groupInfo){
console.log('hey check this out', groupInfo)
if (groupInfo.user_joined) {
var formData = new FormData();
formData.append('user_id', global.currentUser.USER_ID);
formData.append('slug', groupInfo.id);
var adminsIdsArray = []
ajaxPost({
url: 'community',
basePath: 'lumen',
params: formData
}).then(res => {
if(!res.err_code) {
console.log('This is what you need', res)
const communityAdminsArray = res.community_admins
adminsIdsArray = communityAdminsArray.map(function(user) {
return user.user_id
})
console.log('AdminsArray', adminsIdsArray.includes(global.currentUser.USER_ID))
}
})
const menuOptions = adminsIdsArray.includes(global.currentUser.USER_ID) ? ['Switch off notifications', 'Edit group information', 'Quit group'] : ['Switch off notifications', 'Quit group']
const menuHeight = menuOptions.length*40
console.log('options', menuOptions)
return(
<ModalDropdown options={menuOptions} style={styles.dropDownView} dropdownStyle = {styles.dropdownStyle, {height: menuHeight, width: 175}} adjustFrame = {adjustDropDownFrame}
renderSeparator = {renderDropDownSeparator} dropdownTextStyle = {styles.dropDownOptionText}
onSelect = {onSelectingDropDownOption.bind(this, groupInfo)}
showsVerticalScrollIndicator = {false}
>
<View style={styles.dropDownButtonView}>
<Image
source={require('../../Assets/vertical_dots_menu.png')}
resizeMode = {Image.resizeMode.center}
style = {styles.dropDownBaseButton}
/>
</View>
</ModalDropdown>
)
}
else {
return null
}
}
The options in menu loading before data. Can some one tell me, how to pause rendering until data loads.
You should follow this pattern to achieve what you want:
state = {requiredData: null}
fetchDataFunction().then((resp)=>{
...
this.setState({requiredData: resp}) // I assume the response of fetch is what you need
})
render() {
if (this.state.requiredData === null) {
return null // you can use a spinner instead null
} else {
return (
... // your main view
)
}
}