Edit a set of objects using localStorage - javascript

Not sure where to start with this one... I'm creating a basic todo app, using localStorage. (I specially, am not using a backend database).
So far, I can update and display, the objects I have stored locally. And I am displaying them on my page.
form.addEventListener('submit', function(e){
e.preventDefault();
// Set object
let data = {
name: nameInput.value,
url: urlInput.value
};
bookMarksArray.push(data);
console.log("Added bookmark #" + data);
// Saving
localStorage.setItem("bookMarksArray", JSON.stringify(bookMarksArray));
});
However, I also want to able to edit, each item in my DOM. Once edited, I want that specific object, which correlates to this, to be updated in localStorage.
How do I do this?
I'm not sure where to start. Here's a codepen, of my code so far:
https://codepen.io/ReenaVerma1981/pen/LYEPbjL
EG
- if I want to update a URL value to www.google.co.uk
- And this is updated, in the correct object, in localStorage
Here's some psuedo code, is this a good approach?
// List each object as an individual form in DOM
// So I can easily update the input.value, (with a new value)
// The **edit** button, would be a submit button
// Or there's an eventHandler on this button
// Which onClick, takes the input.value, (either name.value or url.value)
// Identifies whether these values, match values in the localStorage object
// And if so, get the object index
// Then update these object values, based on the object index?
// Update localStorage via localStorage.setItem
Here's some example code, I'm writing, to try and do this:
// UPDATE/EDIT EXISTING
const list = document.querySelector('.url-list');
list.addEventListener('click', event => {
if (event.target.classList.contains('js-edit-url')) {
console.log('edit');
const editName = event.target.parentElement.name.value;
const editURL = event.target.parentElement.url.value;
let data = {
name: editName,
url: editURL
};
Object.keys(bookMarksArray).map(function (old_key, index) {
// console.log('old_key',old_key);
let new_key = data;
console.log('data', data);
if (old_key !== new_key) {
Object.defineProperty(bookMarksArray, new_key,
Object.getOwnPropertyDescriptor(bookMarksArray, old_key));
// console.log('bookMarksArray',bookMarksArray);
// localStorage.setItem("bookMarksArray", JSON.stringify(bookMarksArray));
delete bookMarksArray[old_key];
}
});
}
});

I figured it out!
I found a really good example here, using the ES6 way, without mutating original data:
// UPDATE/EDIT EXISTING
const list = document.querySelector('.url-list');
list.addEventListener('click', event => {
if (event.target.classList.contains('js-edit-url')) {
console.log('edit');
const editName = event.target.parentElement.name.value;
const editURL = event.target.parentElement.url.value;
// Find the object index, by looping through and matching name.value
const objIndex = bookMarksArray.findIndex(obj => obj.name === editName);
// make new object of updated object.
const updatedObj = { ...bookMarksArray[objIndex], url: editURL};
// make final new array of objects by combining updated object.
const updatedProjects = [
...bookMarksArray.slice(0, objIndex),
updatedObj,
...bookMarksArray.slice(objIndex + 1),
];
localStorage.setItem("bookMarksArray", JSON.stringify(updatedProjects));
}
});

Related

Optimize Javascript code to find records in array that match criteria

I have a parent component which is a list of records and in one of the child component I have a form that submits data and if successful is added to that list in the parent. Everytime the data is submitted, I need to check if there is an identical record with the same title. This child form component is used to add and edit records, so if the record is edited then I also have to check that it can be submitted with the same name of ofcourse. Below is the code I have and it is working fine but I have been thinking if there is a better way of writing this. Can it be done while going thru the list array the first time instead of going through it once and then going thru it again to check for unique items.
When the data is submitted in the child component (form) I am executing the following functions to see if title field is unique.
const isUniqueTitle = (title) => {
if(activities.find(activity => activity.title.toLowerCase() === title)){
// shows alert
return false;
}
return true;
}
// Child component/form calls this function with the form data
const validateData = data = {
let isUnique = true;
//activities below is available here in the parent
activities.forEach(activity => {
// check for id below tells me that its a record being edited so only do a check if the title
// has been changed else if there is no id then it means its a new record so continue with the
// check
if (activity.id && activity.title != activity.title) {
isUnique = isUniqueTitle(data.title);
} else if (!activity.id) {
isUnique = isUniqueTitle(data.title);
}
return isUnique;
})
}
Please advise, thank you!
You could use a Set to store titles and use its has method to check for the uniqueness of any given title
The Set object lets you store unique values of any type
const titleSet = new Set(activities.map(activity => activity.title.toLowerCase()))
const isUniqueTitle = (title) => {
return titleSet.has(title);
}
// Child component/form calls this function with the form data
const validateData = data = {
//activities below is available here in the parent
activities.forEach(activity => {
// check for id below tells me that its a record being edited so only do a check if the title
// has been changed else if there is no id then it means its a new record so continue with the
// check
if (activity.id && activity.title != activity.title) {
isUniqueTitle(data.title);
} else if (!activity.id) {
isUniqueTitle(data.title);
}
})
}

I want to prevent the localstorage from clear my objects on reload and adding new entries in my form

How do I prevent the local storage from being deleted after reload and insert a new entry from my input fields? I want to display the data that I saved in a list in HTML.
// when I add new items in my form and submit it, the old entries in local-storage gets deleted
let datas = [];
const addEntry = e => {
e.preventDefault();
let data = {
id: document.querySelector("#date").value,
situation: document.querySelector("#situation").value,
mood: document.querySelector("#mood").value,
special: document.querySelector("#special").value,
expectations: document.querySelector("#expectations").value,
fulfilled: document.querySelector("#fulfilled").value,
};
datas.push(data);
console.log(datas);
document.querySelector("form").reset();
localStorage.setItem("smokeEntries", JSON.stringify(datas));
};
let content;
//get object and save in array variable
const getEntriesFromLocalStorage = () => {
content = [JSON.parse(this.localStorage.getItem("smokeEntries"))];
};
document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#submit").addEventListener("click", addEntry);
});
Personally I wouldn't try to keep two arrays (datas and content) in sync as it can cause inconsistency problems like this where you're using one to update and store information in state management and the other displayed visually are out of sync. I'd suggest combining them like this:
let content = [];
const addEntry = e => {
e.preventDefault();
let data = {
id: document.querySelector("#date").value,
situation: document.querySelector("#situation").value,
mood: document.querySelector("#mood").value,
special: document.querySelector("#special").value,
expectations: document.querySelector("#expectations").value,
fulfilled: document.querySelector("#fulfilled").value,
};
content.push(data);
document.querySelector("form").reset();
localStorage.setItem("smokeEntries", JSON.stringify(content));
};
const getEntriesFromLocalStorage = () => {
content = [JSON.parse(localStorage.getItem("smokeEntries"))];
};
document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#submit").addEventListener("click", addEntry);
});
There also may be some problem with setting content (because we're wrapping it in brackets ([]) but it may already be an array -- should be pretty obvious after you run it, remove the brackets if it turns into nested arrays) but I haven't ran the code. Also note that for content to load "state" you'll need to call getEntriesFromLocalStorage() before using the state (or possibly trigger a re-render after).

How to update an IndexedDB item with autoincrement key? [duplicate]

This question already has an answer here:
indexeddb put not update a value, it creates a new (id)
(1 answer)
Closed 2 years ago.
I created an object store with autoIncrement: db.createObjectStore("items", {autoIncrement:true});
Now I want to be able to update an item given its key and a new value, so I wrote this function:
let updateItem = (key, newData) => {
let objectStore = db.transaction(["items"], "readwrite").objectStore("items");
let request = objectStore.get(key);
request.onsuccess = (e) => {
let data = e.target.result;
Object.assign(data, newData);
let requestUpdate = objectStore.put(data);
};
}
However, instead of updating the value, it creates a new item with the new data. I think it makes sense since e.target.result does not contain any information about its key. So how do I update an element in such an object store?
You need to add a key as a second parameter, like objectStore.put(data, key).
key
The primary key of the record you want to update (e.g. from IDBCursor.primaryKey). This is only needed for object stores that have an autoIncrement primary key, therefore the key is not in a field on the record object. In such cases, calling put(item) will always insert a new record, because it doesn't know what existing record you might want to modify.
-- IDBObjectStore.put() - Web APIs | MDN
I found another solution using cursor.update():
let updateItem = (key, newData) => {
let objectStore = db.transaction("items","readwrite").objectStore("items");
objectStore.openCursor().onsuccess = (e) => {
let cursor = e.target.result;
if (cursor && cursor.key == key) {
cursor.update(Object.assign(cursor.value, newData));
cursor.continue();
}
};
}

Too many re-renders when accessing data from an API in React

I am making a get request to an API that is linked to my database.
dataApi is a really big object with a lot of objects and arrays nested within it.
Some of the entries from the database are not having the full details that I need so I am filtering them to only show those with a length of > 5.
Now the issue is when I try to get the name of each entry which is split into either Tag1, Tag2 or Tag3.
Before this when I was accessing all the entries and getting the items within them there was no issue.
But when I try to filter them by the name and store the objects corresponding to that name in its state this issue arrises.
Edit:
When I console.log(arr1) it shows all the data but the moment I set the state to it it causes the error.
// Data from all entries in database
const [dataApi, setDataApi] = useState();
// Data for each of the tags
const [tag1, setTag1] = useState();
const [tag2, setTag2] = useState();
const [tag3, setTag3] = useState();
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
setDataApi(data);
});
}, []);
const getTagDetails = data => {
const arr1 = [];
const arr2 = [];
const arr3 = [];
data &&
data.forEach(d => {
// Entries into the database which do not have any tag information
// have a size of 5 and those with all the details have a size of 6
const sizeOfObject = Object.keys(d).length;
// Only need database items with all the details
if (sizeOfObject > 5) {
const name = d["tags"]["L"][0]["M"]["name"]["S"];
// Split the data for the tags into their respective name
// Will be used to set individual datasets for each tag
if (name === "Tag1") {
arr1.push(d);
}
if (name === "Tag2") {
arr2.push(d);
}
if (name === "Tag3") {
arr3.push(d);
}
}
});
setTag1(arr1);
setTag2(arr2);
setTag3(arr3);
};
getTagDetails(dataApi);
I guess the problem is you call getTagDetails(dataApi) inside of file so it causes this infinite rendering problem
Instead remove getTagDetails and try to call this functions after API resolved.
useEffect(() => {
axios.get(URL).then((res) => {
const data = res.data;
getTagDetails(data)
});
}, []);
I think your problem is the way you have structured your getTagDetails function. Each time you render, you call getTagDetails() and the first thing you do is create a new array for each tag. When you call setTag with the new array, it will rerender. You'll probably want to move the getTagDetails logic into the effect so it only runs once on mount (or add a dependency to the dependency array if you need to update on new data)

React JS Local Storage update on view change

How can I update certain properties of a local storage item or object as new data is inputted throughout the user journey and not lose what was previously entered or if the user decides to update?
My journey of 5 containers consisting of asking the user to input the following:
Name: string
Avatar: integer
Favourite Genres: multiple strings
On the first view I have created the local storage object / item that sets the name within the handleSubmit function.
handleSubmit(event) {
event.preventDefault();
//Profile object
let profile = { 'name': this.state.name, 'avatar': null, 'genres': '' };
// Put the object into storage
localStorage.setItem('profile', JSON.stringify(profile));
// Retrieve the object from storage
var retrievedObject = localStorage.getItem('profile');
//Log object
console.log('retrievedObject: ', JSON.parse(retrievedObject));
//On form submission update view
this.props.history.push('/profile/hello');
}
On my second view I want to update only the avatar property and maintain what the user had inputted in the previous view.
I'm doing this within the handleSelect function like so:
handleSelect(i) {
let selectedAvatarId;
let avatars = this.state.avatars;
avatars = avatars.map((val, index) => {
val.isActive = index === i ? true : false;
return val;
});
this.setState({
selectedAvatarId: selectedAvatarId
})
//Profile object
let profile = { 'avatar': i };
//Update local storage with selected avatar
localStorage.setItem('profile', JSON.stringify(profile));
}
You will need to read the existing value from localStorage, parse it as JSON and then manipulate the data, and write it back. There are numerous libraries out there for easily working with localStorage, but something along the lines of this should work as a generic function:
function updateProfile = (updatedData) => {
const profile = JSON.parse(localStorage.getItem('profile'));
Object.keys(updatedData).forEach((key) => {
profile[key] = updatedData[key];
});
localStorage.setItem('profile', JSON.stringify(profile));
}
If you use object spread, it could look a lot cleaner too:
function updateProfile = (updatedData) => {
const profile = {
...JSON.parse(localStorage.getItem('profile')),
...updatedData
};
localStorage.setItem('profile', JSON.stringify(profile));
}
There should probably be some safety checks in the above code, but hopefully gives you an idea for a starting point.
The only option as far as I know is to get it as a Json, amend accordingly and then save it is again.

Categories