Check Box Update Request,ReactJS - javascript

I'm making simple To Do List app,Everything is working.I just want to make sure I'm doing it right without any mistakes.
I'm concerned about Check box update part,Please check the code and tell me if I'm doing anything wrong.
Here is the put method for Checkboxes
checkBoxRouteUpdate = () => {
let {todos} = this.state
let newArray = [...todos]
axios
.put(`http://localhost:8080/checkEdit/`, {
checked: newArray.every(todo => todo.checked)
}).then((res) => {
console.log("res", res);
})
.catch((err) => {
console.log("err", err);
});
}
checking all of them
checkAllCheckBox = () => {
let {todos} = this.state
let newArray = [...todos]
if (newArray.length !==0) {
newArray.map(item => {
if (item.checked === true) {
return item.checked = false
} else {
return item.checked = true
}
})
this.checkBoxRouteUpdate()
this.setState({todos: newArray})
}
}
Checking single Check Box
checkSingleCheckBox = (id) => {
let {todos} = this.state
let newArray = [...todos]
newArray.forEach(item => {
if (item._id === id) {
item.checked = !item.checked
axios
.put(`http://localhost:8080/edit/${id}`,{
checked:item.checked
})
.then(res => {
this.setState({todos: newArray})
console.log('res',res)
})
.catch((err) => {
console.log("err", err);
});
} else {
}
})
}
Deleting Only Checked Items
deleteAllChecked = () => {
const todos = this.state.todos.filter((item => item.checked !== true))
axios
.delete('http://localhost:8080/deleteAllChecked')
.then((res) => {
this.setState({ todos,
pageCount: Math.ceil(todos.length / 10)})
console.log("res", res);
})
.catch((err) => {
console.log("err", err);
});
}

You can check/uncheck them another way
this.checkBoxRouteUpdate()
this.setState(state => ({
...state,
todos: state.todos.map(todo => ({
...todo,
checked: !item.checked
}))
}))
I think you should delete after api returns ok status
.then((res) => {
this.setState(state => {
const todos = state.todos.filter((item => item.checked !== true));
return {
...state,
todos,
pageCount: Math.ceil(todos.length / 10)
}
})

I add a lot of comments, some of these some just another way to do what you do and others are personal preferences, but the most important is that you can see alternatives ways to do things :).
checkBoxRouteUpdate = () => {
const todos = [...this.state.todos] // Better use const and initialize the array of objects directly
/*since you will use this array just in one place, is better if you iterate in
the [...todos] directly without save it in a variable
let newArray = [...todos]
*/
axios
.put(`http://localhost:8080/checkEdit/`, {
checked: todos.every(({checked}) => checked) // here you can use destructuring to get checked
}).then((res) => {
console.log("res", res);
})
.catch((err) => {
console.log("err", err);
});
}
```
checking all of them
```
checkAllCheckBox = () => {
const todos = [...this.state.todos] // Better use const and initialize the array of objects directly
// let newArray = [...todos] same as in the first function,
// isn't neccesary this if because if the array is empty, the map doesn't will iterate
// if (newArray.length !==0) {
/* this is optional, but you can write this like
const modifiedTodos = [...todos].map(({checked}) => checked = !checked)
*/
/* In general, is better use const when possible because in this way
you will reassign a variable just when is necessary, and this is related with
avoid mutate values. */
const modifiedTodos = todos.map(item => {
if (item.checked === true) {
return item.checked = false
} else {
return item.checked = true
}
})
this.checkBoxRouteUpdate()
this.setState({ todos: modifiedTodos })
}
// Checking single Check Box
checkSingleCheckBox = (id) => {
// since you need be secure that the todos is an array, you can do this instead of the destructuring
const todos = [...this.state.todos]
// same as in the above function
// let newArray = [...todos]
// Here is better to use destructuring to get the _id and checked
[...todos].forEach(({checked, _id}) => {
/* this is totally personal preference but I try to avoid put a lot of code inside an if,
to do this, you can do something like:
if(_id !== id) return
and your code doesn't need to be inside the if
*/
if (_id === id) {
/* this mutation is a little difficult to follow in large codebase, so,
is better if you modified the value in the place you will use it*/
// checked = !item.checked
axios
.put(`http://localhost:8080/edit/${id}`, {
checked: !checked
})
.then(res => {
this.setState({ todos: todos }) // or just {todos} if you use the object shorthand notation
console.log('res', res)
})
.catch((err) => {
console.log("err", err);
});
}
// this else isn't necessary
// else {
// }
})
}
// Deleting Only Checked Items
deleteAllChecked = () => {
const todos = this.state.todos.filter((item => item.checked !== true))
/* Another way to do the above filtering is:
const todos = this.state.todos.filter((item => !item.checked))
*/
axios
.delete('http://localhost:8080/deleteAllChecked')
.then((res) => {
this.setState({
todos,
pageCount: Math.ceil(todos.length / 10)
})
console.log("res", res);
})
.catch((err) => {
console.log("err", err);
});
}

Related

How can I make the function work with the updated state?

I have a handleValid function to validate a form, and when I click submit, the function is triggered for validation and calls handleSelfValidation, in the handleSelfValidation app they write the form state information and change the state, but handleInfoCheck is looking at the previous state, and for this reason I need to click twice to "Send".
const handleValid = () => {
members
.filter((member: Tourist) => {
return member.createdIn === touristCreatedIn && !member.isEmployee;
})
.forEach((member: any, index: any) => {
personSchema
.validate(member, { abortEarly: false })
.then(() => {
setFieldError({
[index]: {}
})
})
.catch((errs: any) => {
setFieldError({})
errs?.inner?.forEach((err: any) => {
setFieldError((prev)=> ({
...prev,
[index]: {
...prev[index],
[err.path]: err.message,
},
}))
});
});
personSchema
.isValid(member)
.then((v: any) => {
console.log('тут', v, index)
handleSelfValidation(v, index); //isFormValid - true
})
.catch((err: any) => {
// eslint-disable-next-line
console.error('TouristData YUP isValid Err', err);
});
});
setTimeout(handleInfoCheck);
};
const handleSelfValidation = (isFormValid: boolean, formIndex: number) => {
console.log(isFormValid, formIndex, 'test')
setIsFormsValid((prev) => ({
...prev,
[formIndex]: isFormValid,
}))
};
const handleInfoCheck = () => {
setFirstVisit();
if (
Object.values(isFormsValid).every((item: any) => {
return item === true;
})
) {
switch (permissionType) {
case 'tour':
history.push(`${addTourUrl}/tour-data`);
break;
case PERMISSION_TYPE_TRANZIT:
history.push(`${addTourUrl}/tranzit-data`);
break;
default:
history.push(`${addTourUrl}/tour-data`);
break;
}
}
};
Issue
The issue here is that React state updates are asynchronously processed, and the state from the current render cycle is closed over in handleValid/handleInfoCheck callback scope.
Solution
Allow the isFormsValid state update to occur in handleSelfValidation and use a useEffect hook with a dependency on isFormsValid to run the additional code.
const handleValid = () => {
members
.filter((member: Tourist) => {
return member.createdIn === touristCreatedIn && !member.isEmployee;
})
.forEach((member: any, index: any) => {
...
personSchema
.isValid(member)
.then((v: any) => {
console.log('тут', v, index)
handleSelfValidation(v, index); // <-- updates state
})
.catch((err: any) => {
// eslint-disable-next-line
console.error('TouristData YUP isValid Err', err);
});
});
};
...
useEffect(() => {
if (isFormsValid) {
handleInfoCheck();
}
}, [handleInfoCheck, isFormsValid]);

Try download img from firebase storage which link is firebase database

I tried to download the image which is in firebase storage which link is store in database. When I tried to download the image, it takes more time to execute while for loop is completed.
Is there any process that somehow I download in time which doesn't make the function really slow? I already solve this issue using setTimeout but I hope there may be a better solution than mine. Help me! thank you!
export const shampooHandler = () => {
return (dispatch) => {
dispatch(shampooStart());
const data = [];
const imgList = [];
fire
.database()
.ref()
.child("Shampoo")
.once("value")
.then((response) => {
for (let i = 0; i < response.val().length; i++) {
fire.storage().refFromURL(response.val()[i].img).getDownloadURL().then((image) => {
imgList.push(image);
})
.catch((error) => {
dispatch(shampooError(error));
});
setTimeout(() => {
name = response.val()[i].name;
description = response.val()[i].description;
value = response.val()[i].value;
img = imgList[i];
data.push({ name, description, value, img });
if (i === (response.val().length - 1)) {
dispatch(shampooSuccess(data));
}
}, 3000);
}
})
.catch((error) => {
dispatch(shampooError(error));
});
};
};
I spend a day finding a right solution for it. It may help someone to find solution in future. Thanks guys for giving a thought and specially DougStevensen to tiggering me an idea
export const shampooHandler = () => {
return (dispatch) => {
dispatch(shampooStart());
const data = [];
const imglist = [];
fire.database().ref().child("Shampoo").once("value").then((response) => {
response.val().forEach(element => {
const promise = imageUrlHandler(element.img).then(url => {
return url;
}).catch(error =>{
dispatch(shampooError(error));
})
imglist.push(promise);
//all the promise call to download the images
Promise.all(imglist).then(items =>{
const dataCollection = {
name: element.name,
description: element.description,
value: element.value,
img: items[items.length - 1]
}
data.push(dataCollection);
if(data.length === response.val().length){
dispatch(shampooSuccess(data));
}
}).catch(err =>dispatch(shampooError(err)));
})
}).catch(error => {
dispatch(shampooError(error));
})
}
}
export const imageUrlHandler = (databaseUrl) => {
return new Promise((resolve,reject)=> {
fire.storage().refFromURL(databaseUrl).getDownloadURL().then((url) => {
resolve(url);
})
.catch((error) => {
reject(error)
});
})
}

How to filter items after fetching data?

I am trying to filter some data from a rest api that I made.
The data is definitely being filtered if I get it locally exactly like this (have a look at the "products" file in this code sandbox that I made): https://codesandbox.io/s/magical-banzai-p10e9
When I try to fetch the data, it does not update the UI with the filtered products. I am guessing that it has to do with a life cycle hook that I am not using but I cannot figure out how to make it work.
This is how I am fetching the data:
componentDidMount () {
axios
.get('http://localhost:4000/api/v1/products')
.then((res) => {
let products = res.data.data.data;
this.setState({
products,
filteredProducts: products,
isLoading: false
})
})
.catch(function (error) {
console.log(error);
})
}
This is the function I am using for filtering the above data:
handleFormSubmit = event => {
event.preventDefault();
const selectedSizes = [...this.selectedCheckboxes];
const shallowCopy = [...this.state.filteredProducts];
let filteredProducts = shallowCopy.filter(product =>
selectedSizes.every(size =>
product.stock.some(s => s.stock > 0 && s.size === size)
)
);
let filteredGender = filteredProducts.filter(product => {
return product.gender.some((item) => {
return item[this.selectedGender] === false ? null : product;
});
});
let filteredData = filteredGender.filter(product => {
return product.brand.includes(this.state.activeBrand);
});
let sortedPrice = filteredData.sort((a, b) => {
return this.state.sortBy === "min"
? a.price - b.price
: b.price - a.price;
});
this.setState({
filteredProducts: sortedPrice
});
};
The UI is in the code sandbox I provided.

Why am I unable to re-add data with the same name after removing it with Firebase?

In my app, users are able to add a loved one, like so:
addLovedOne(event) {
const {
lovedOne,
} = this.state;
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
db.addLovedOne(user.uid, lovedOne)
.then(() => {
this.setState({ lovedOne: '' });
this.refreshLovedOnes();
})
.catch(error => {
this.setState(byPropKey('error', error));
});
} else {
unsubscribe();
}
});
event.preventDefault();
}
Naturally, users are able to remove loved ones, like so:
removeLovedOne(event) {
const lovedOne = event.target.id;
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
const ref = firebase.database().ref(`users/${user.uid}/lovedOnes`);
const removeLovedOne = ref.orderByChild('lovedOne').equalTo(lovedOne);
removeLovedOne.on('value', (snapshot) => {
const lovedOneId = snapshot.node_.children_.root_.key;
db.removeLovedOne(user.uid, lovedOneId, lovedOne)
.then(() => {
this.refreshLovedOnes();
})
.catch(error => {
this.setState(byPropKey('error', error));
});
});
} else {
unsubscribe();
}
});
}
Here is what the queries look like:
export const addLovedOne = (userId, lovedOne) => (
db.ref(`users/${userId}/lovedOnes`).push({
lovedOne,
})
);
export const removeLovedOne = (userId, lovedOneKey) => (
db.ref(`users/${userId}/lovedOnes/${lovedOneKey}/lovedOne`).remove()
);
Here is what my schema looks like:
Any idea why I'm not able to add a loved one with the same name after removing it without refreshing the page?

How to re-order Firestore 'modified' changes using DocumentChange.newIndex?

I'm using the web API for Firestore to perform a simple query ordered on a date property formatted as a string ('2017-12-30'). I use the onSnapshot() method to subscribe as a listener to document changes. The initial population of list of results works as expected - the order is correct.
As I make changes to the data, the callback then gets called with a change type of 'modified'. If any of the changes affects the date property, then I have no way of re-ordering the item in the list of results - unlike the old Realtime Database. That is, until I saw the newIndex and oldIndex properties of DocumentChange. They are undocumented for the Web API (https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentChange), but are documented as part of the Node.js API (https://cloud.google.com/nodejs/docs/reference/firestore/0.10.x/DocumentChange).
So, my problem seemed to be solved - except that in practice the values in newIndex and oldIndex seem to be largely random and bear no relation to the actual order if I refresh the query. I can't make out any pattern that would explain the index values I get back.
Has anyone used DocumentChange.newIndex and DocumentChange.oldIndex successfully? If not, how would you reorder results in subscribers as a result of changes?
const query = firestore.collection(`users/${uid}/things`).
orderBy('sortDate', 'desc').limit(1000)
query.onSnapshot(snapshot => {
snapshot.docChanges.forEach(change => {
if (change.type === "added") {
dispatch(addThing({
id: change.doc.id,
...change.doc.data()
}, change.newIndex)
}
if (change.type === "modified") {
dispatch(changeThing({
id: change.doc.id,
...change.doc.data()
}, change.oldIndex, change.newIndex))
}
if (change.type === "removed") {
dispatch(removeThing(change.doc.id, change.oldIndex))
}
})
})
The original problem I had with the DocumentChange indexes was due to a couple of bugs elsewhere in my code. As I didn't find any examples of this in use outside of the Node.js Firestore docs, here's the test code I used to verify its correct behaviour (ES6). It assumes firebase has been initialized.
cleanTestData = (firestore, path) => {
console.log("Cleaning-up old test data")
var query = firestore.collection(path)
return query.get().then(snapshot => {
const deletePromises = []
if (snapshot.size > 0) {
snapshot.docs.forEach(function(doc) {
deletePromises.push(doc.ref.delete().then(() => {
console.log("Deleted ", doc.id)
}))
});
}
return Promise.all(deletePromises)
}).then(() => {
console.log("Old test data cleaned-up")
})
}
createTestData = (firestore, path) => {
console.log("Creating test data")
const batch = firestore.batch()
const data = {
a: '2017-09-02',
b: '2017-12-25',
c: '2017-10-06',
d: '2017-08-02',
e: '2017-09-20',
f: '2017-11-17'
}
for (const id in data) {
batch.set(firestore.collection(path).doc(id), { date: data[id] })
}
return batch.commit().then(() => {
console.log("Test data created");
}).catch(error => {
console.error("Failed to create test data: ", error);
})
}
subscribe = (firestore, path) => {
const datesArray = []
return firestore.collection(path).orderBy('date', 'asc').onSnapshot(snapshot => {
snapshot.docChanges.forEach(change => {
console.log(change.type, "id:", change.doc.id,
"; date:", change.doc.data().date,
"; oldIndex:", change.oldIndex, "; newIndex:", change.newIndex,
"; metadata: ", change.doc.metadata)
if (change.oldIndex !== -1) {
datesArray.splice(change.oldIndex, 1);
}
if (change.newIndex !== -1) {
datesArray.splice(change.newIndex, 0, change.doc.data().date);
}
console.log(" -->", JSON.stringify(datesArray))
})
})
}
update = (firestore, path) => {
console.log("Updating test data")
return firestore.collection(path).doc('d').set({date: '2018-01-02'}).then(() => {
console.log("Test doc 'd' updated from '2017-08-02' to '2018-01-02'")
})
}
query = (firestore, path) => {
var query = firestore.collection(path).orderBy('date', 'asc')
return query.get().then(snapshot => {
const dates = []
if (snapshot.size > 0) {
snapshot.docs.forEach(function(doc) {
dates.push(doc.data().date)
});
}
console.log("Fresh query of data: \n -->", JSON.stringify(dates))
})
}
handleStartTest = e => {
console.log("Starting test")
const firestore = firebase.firestore()
const path = `things`
let unsubscribeFn = null
unsubscribeFn = this.subscribe(firestore, path)
this.cleanTestData(firestore, path).then(() => {
return this.createTestData(firestore, path)
}).then(() => {
return this.update(firestore, path)
}).then(() => {
return this.query(firestore, path)
}).then(() => {
unsubscribeFn()
console.log("Test complete")
}).catch((error) => {
console.error("Test failed: ", error)
})
}
This is the way it worked for me:
onSnapshot((ref) => {
ref.docChanges().forEach((change) => {
const { newIndex, oldIndex, doc, type } = change;
if (type === 'added') {
this.todos.splice(newIndex, 0, doc.data());
// if we want to handle references we would do it here
} else if (type === 'modified') {
// remove the old one first
this.todos.splice(oldIndex, 1);
// if we want to handle references we would have to unsubscribe
// from old references' listeners and subscribe to the new ones
this.todos.splice(newIndex, 0, doc.data());
} else if (type === 'removed') {
this.todos.splice(oldIndex, 1);
// if we want to handle references we need to unsubscribe
// from old references
}
});
});
source

Categories