I'm using React Native and firebase for the application, where I'm using 'on' for listening to database changes, but whenever data is updated, my data doubles (getting duplicates of every object). I tried resetting the data before forEach loop but it returns the empty data on the last iteration. I was using array earlier and then I tried using Set but still the same result. Any help will be appreciated. Thanks
Here is my code:
friendPost = () => {
let that = this;
let res = new Set();
firebase.database().ref("/Manifest User/"+this.state.currentUsername.charAt(0).toUpperCase()+ "/"+
this.state.currentUsername+"/Friends").on("value", function (snapshot) {
// let result=[];
snapshot.forEach(function (childSnapshot) {
if(childSnapshot.key !== 'Friend Requests'){
res=[];
firebase.database().ref("/Manifest User/"+childSnapshot.key.charAt(0).toUpperCase()+"/"+childSnapshot.key
+"/Profile Posts/").on("value", function (postSnapshot) {
postSnapshot.forEach(function(miniSnapshot){
if(miniSnapshot.key !== '~default') {
let url;
let urlTemp = miniSnapshot.val().post.match(/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/);
if (urlTemp !== null){
url = urlTemp[0];
if (urlTemp[0].includes("youtu")) {
url = "https://www.youtube.com/embed/"+urlTemp[0].substr(urlTemp[0].lastIndexOf("/")+1);
}
else if (urlTemp[0].includes("vimeo.com")){
url = "https://player.vimeo.com/video/"+urlTemp[0].substr(urlTemp[0].lastIndexOf("/")+1);
}
}
let postString = miniSnapshot.val().post
.replace(/(http|https|ftp|ftps)\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(\/\S*)?/, '');
res.push({
postDate: miniSnapshot.key,
postImage: miniSnapshot.hasChild('postImage') ? miniSnapshot.val().postImage : '',
post: postString,
username: miniSnapshot.hasChild('postUserName') ? miniSnapshot.val().postUserName : '',
comments: miniSnapshot.hasChild('postComments') ? miniSnapshot.val().postComments : [],
likes: miniSnapshot.hasChild('postLikes') ? miniSnapshot.val().postLikes : [],
url: url,
actualPostString: miniSnapshot.hasChild('post') ? miniSnapshot.val().post : '',
});
}
});
// alert(JSON.stringify(res))
that.setState({friendPosts: Array.from(res)});
}.bind(this));
}
});
})
};
A reason for this to be happening is your component re-rendering. The code where you are calling this function is called twice i.e. on a re-render.
Related
I am fetching recipes from a recipe app and id like to insert certain objects from the returning json result onto my state with setstate. I know how to do one of these but im having trouble figuring out how to map the results on to my state. Can anyone help me on this?
The code for the issue is here. I have changed my api key and code for security
componentDidMount() {
let url = `https://api.edamam.com/search?q=banana&app_id=chjhvje1&app_key=b67djhhvhvhaef`;
fetch(url)
.then((response) => {
return response.json();
})
.then((data) => {
let recipeUIState = [ ...this.state.RecipeUI ];
recipeUIState[0].title = data.hits[0].recipe.label;
recipeUIState[0].thumbnail = data.hits[0].recipe.image;
recipeUIState[0].href = data.hits[0].recipe.url;
this.setState({ RecipeUI: recipeUIState });
console.log(data.hits[0].recipe);
});
}
State is as follows-
export default class RecipeUI extends Component {
constructor(props) {
super(props);
this.state = {
food: '',
RecipeUI: [ { title: '' } ]
// thumbnail: '', ingredients: '', href: ''
};
this.search = this.search.bind(this);
}
reponse from API is attached as image
data.hits.forEach(({ recipe }) => {
// We get the original state every before it's updated in the iteration
const recipeUIState = [...this.state.RecipeUI];
// Check if there's an existing recipe with the same title
const idx = recipeUIState.findIndex(r => r.title === recipe.title);
// Helper object to create a recipe from the current iteration
const currentRecipe = {
title: recipe.label,
thumbnail: recipe.image,
href: recipe.url
};
// `findIndex` returns -1 if no entry was found, otherwise it returns the index
if (idx < 0) {
// No existing recipe was found, append the new recipe to the original state
return this.setState({
recipeUIState: [...recipeUIState, ...currentRecipe]
});
}
// Recipe already existed, create a new recipe by overwriting
// the object at the index we found earlier
const newRecipeUIState = {
...recipeUIState[idx],
...currentRecipe
};
// Replace the recipe at found index
recipeUIState[idx] = newRecipeUIState;
this.setState({ recipeUIState });
});
Something like this? could probably be simplified using Array#reduce but I don't feel too comfortable using it.
I have a helper function that builds object with appropriate query properties. I use this object as a body in my promise request. What is the most elegant way for refactoring multiple if statements? Here is a function:
getQueryParams = (query, pagination, sorting) => {
let queryParam = {}
if (pagination && pagination.pageNumber) {
queryParam.page = `${pagination.pageNumber}`
}
if (pagination && pagination.rowsOnPage) {
queryParam.size = `${pagination.rowsOnPage}`
}
if (query) {
const updatedQuery = encodeURIComponent(query)
queryParam.q = `${updatedQuery}`
}
if (sorting) {
queryParam.sort = `${sorting.isDescending ? '-' : ''}${sorting.name}`
}
return service.get(`/my-url/`, queryParam).then(result => {
return result
})
}
If service checks its parameters (as it should), you could benefit from the default parameters. Something like this:
const getQueryParams = (
query = '',
pagination = {pageNumber: 0, rowsOnPage: 0},
sorting = {isDescending: '', name: ''}
) => {
const queryParam = {
page: pagination.pageNumber,
size: pagination.rowsOnPage,
q: encodeURIComponent(query),
sort: `${sorting.isDescending}${sorting.name}`
}
return ...;
};
A live example to play with at jsfiddle.
This is an idea how it could looks like, but you need to adopt your params before:
const query = new URLSearchParams();
Object.keys(params).forEach(key => {
if (params[key]) {
query.append(key, params[key]);
}
});
While deleting the data from firebase database null value is getting inserted into firebase.
deleteImag = e => {
const val = e.target.value;
var x = '';
const rootRef = fire.database().ref();
const fooRef = rootRef.child(`assets/${this.state.id}`);
fooRef.on("value", snap => {
const foo = snap.val();
if (foo !== null) {
Object.keys(foo).forEach(key => {
if (foo[key].id = val) {
console.log(key);
x = key;
// The Object is foo[key]
var res = null;
console.log(foo[key].id);
fire.database().ref(`assets/${this.state.id}/${x}`).remove();
}
});
}
});
};
JSON:
{
"hero" : {
"img" : "http://aws-website-testing-slv0n.s3-website-us-east-1.amazonaws.com/images/CornHole3.jpg"
},
"id" : "urn:microsense:cms:asset:80c71fdb-0d32-41de-8170-8e35409b8e63",
"purpose" : "activity",
"thumbnail" : {
"img" : "http://aws-website-testing-slv0n.s3-website-us-east-1.amazonaws.com/images/CornHole3.jpg"
}
}, null, null, {
enter image description here
There was array issue. When we remove an array in Firebase it replace that element with null. So the above article was helpful for me. Thanks
I have modal component with form. I want to inform fields of this form that form data was successfully sent to database and clear its fields.
Component code:
//ItemModal.js
addItem(e) {
e.preventDefault();
const item = {
id: this.props.itemsStore.length + 1,
image: this.fileInput.files[0] || 'http://via.placeholder.com/350x150',
tags: this.tagInput.value,
place: this.placeInput.value,
details: this.detailsInput.value
}
console.log('addded', item);
this.props.onAddItem(item);
this.fileInput.value = '';
this.tagInput.value = '';
this.placeInput.value = '';
this.detailsInput.value = '';
this.setState({
filled: {
...this.state.filled,
place: false,
tags: false
},
loadingText: 'Loading...'
});
}
...
render() {
return (
<div className="text-center" >
<div className={"text-center form-notification " + ((this.state.loadingText) ? 'form-notification__active' : '' )}>
{(this.state.loadingText) ? ((this.props.loadingState === true) ? 'Item added' : this.state.loadingText) : '' }
</div>
)
}
action.js
export function onAddItem(item) {
axios.post('http://localhost:3001/api/items/', item )
.then(res => {
dispatch({type:"ADD_ITEM", item});
dispatch({type:"ITEM_LOADED", status: true});
})
}
helper.js
else if (action.type === 'ITEM_LOADED') {
const status = action.status;
return {
...state,
isItemLoaded: status
}
}
Currently I have few issues with my code:
1. field are clearing right after click, but they should clear after changing state of loadingState. I tried to check it in separate function on in componentWillReceiveProps whether state is changed and it worked, but I faces another problem, that after closing this modal there were errors, that such fields doesn't exist.
2. loadingText should become '' (empty) after few seconds. Tried same approach with separate function and componentWillReceiveProps as at first issue.
In constructor keep a copy of your initial state in a const as follows:
const stateCopy = Object.create(this.state);
When your ajax request completes, in the sucess callback you can reset the state with this copy as follows:
this.setStae({
...stateCopy
});
One of the few ways to achieve this is to use async await which will resolve the promises and then return the value after that you can clear the values
1st approach using the async await
Here is the example
handleSubmit = async event => {
event.preventDefault();
// Promise is resolved and value is inside of the response const.
const response = await API.delete(`users/${this.state.id}`);
//dispatch your reducers
};
Now in your react component call it
PostData() {
const res = await handleSubmit();
//empty your model and values
}
Second approach is to use the timer to check the value is changed or not
for this we need one variable add this to the service
let timerFinished=false;
one function to check it is changed or not
CheckTimers = () => {
setTimeout(() => {
if (timerFinished) {
//empty your modal and clear the values
} else {
this.CheckTimers();
}
}, 200);
}
on your add item change this variable value
export function onAddItem(item) {
axios.post('http://localhost:3001/api/items/', item)
.then(res => {
timerFinished = true;
dispatch({
type: "ADD_ITEM",
item
});
dispatch({
type: "ITEM_LOADED",
status: true
});
})
}
and here is how we need to call it.
PostData = (items) => {
timerFinished = false;
onAddItem(items);
this.CheckTimers();
}
If you check this what we done is continuously checking the variable change and emptied only once its done.
One thing you need to handle is to when axios failed to post the data you need to change the variable value to something and handle it, you can do it using the different values 'error','failed','success' to the timerFinished variable.
I have a container that holds search bar inside a form element consisting of two inputs, from and to, and a button.
On my form submit function, I create an OBJECT called query and it equals to:
const query = {
from : this.state.from,
to : this.state.to
};
then I pass this query object as an argument to an action that have created :
this.props.fetchPlaces(query);
In my action.js inside my fetchPlaces I have :
var skypicker = {
flyFrom : query.flyFrom,
flyTo : query.toCity,
};
I want to be able to pass flyFrom and flyTo to an api that later returns flights from flyFrom to flyTo which return results as JSON!
ofcourse i have to parse them to the URl, but at the current state flyFrom and flyTo are undefined, Am I doing this currectly?
You're accessing properties that you haven't set. The names differ between query and skypicker.
Try creating skypicker like this:
var skypicker = {
flyFrom: query.from,
flyTo: query.to
};
It’s query.from and query.to but not query.flyFrom and query.toCity
Check right one below
var skypicker = {
flyFrom : query.from,
flyTo : query.to
};
I generally do this
request body:
let data = {};
data.firstName = this.state.firstName;
data.lastName = this.state.lastName;
data.email = this.state.email;
data.password = this.state.password;
data.sex = this.state.sex;
this.props.registerUser(data);
action call:
export function registerUser(data) {
return dispatch => {
dispatch(registerUserRequest());
return ajax.post(URL_PREFIX + '/auth/register/', data)
.then(res => {
dispatch(registerUserSuccess(res.data))
setTimeout(function(){
dispatch(push('/login'))
}, 2000)
})
.catch(errors => {
dispatch(registerUserError(errors))
})
}
}