Setting and modifying array and pushing to localStorage creates nested array - javascript

I have a function that takes a string from a search field during runtime, and stores it to localstorage. Since we want to store all search strings from the end user to record it, we need to get the current data from localstorage, and add the latest search string.
Here is my code:
const setDatatoLocalStorag = (searchQuery: string) => {
let searchHistory = localStorage.getItem("searchHistory");
let searchQueryArr = [];
if (searchHistory) {
JSON.parse(searchHistory);
searchQueryArr.push(searchQuery, searchHistory);
} else {
searchQueryArr.push(searchQuery);
}
localStorage.setItem("searchHistory", JSON.stringify(searchQueryArr));
}
Lets assume we run the function twice, with the searchQuery "dog" and "cat". This is how it will look like in localstorage:
["cat","[\"dog\"]"]
I believe localstorage will get the item as string "[myData]" which will cause the error. How to properly handle this?
I have tried to follow How to store an array of objects in Local Storage? withous success.

The problem is you aren't assigning JSON.parse(searchHistory); to a variable. I think what you want to do is this:
var searchQueryArr = ['dog'];
localStorage.setItem("searchHistory", JSON.stringify(searchQueryArr));
var searchHistory = JSON.parse(localStorage.getItem("searchHistory") || '[]');
console.log(searchQueryArr, searchHistory);
Note I wasn't able to get this to work with the inline editor, but I did try it on a real server and it worked.

Make a setter/getter pair that hide the encoding/unencoding. Then add a higher-level push that does a get and a set...
// const pretend local storage, keys => strings
const myLocalStorage = {}
function mySetItem(key, value) {
// use the actual local storage setItem() here
myLocalStorage[key] = JSON.stringify(value);
}
function myGetItem(key) {
// use the actual local storage getItem() here
return JSON.parse(myLocalStorage[key]);
}
function myPush(key, value) {
let current = myGetItem(key);
current.push(value)
mySetItem(key, current)
}
// test
const key = 'myKey'
mySetItem(key, []);
myPush(key, { message: 'hello' })
myPush(key, { message: 'dolly' })
console.log(myGetItem(key))

Related

How do I change an object's value in an IndexedDB index?

Is it possible to update an object's value within an IndexedDB index without cloning, deleting, or putting a new entry? Theoretically something like the following snippet would do the trick, though it probably would not delete until the put was confirmed. But it looks like overkill to me. It looks like it would be a nightmare to do any error handling on.
const objectStore = db.transaction([objectStoreName], 'readwrite')
.objectStore(objectStoreName);
const requestGet = objectStore.get(index);
requestGet.onsuccess = (event: any) => {
const value = event.target.result.value // Store old value
const requestDelete = objectStore.delete(index);
requestDelete.onsuccess = (event: any) => {
const requestPut = objectStore
.put({index: 'New Index Value', value: value}); // Put back using new index
};
};
You cannot directly change values in an object store's index. You can change the values of an object in an object store, and IndexedDB will propagate your changes to related indices. Indices are essentially read-only.
It is possible since you specify your index, otherwise, an other logic may be necessary.
As you should know, the IDBObjectStore has a method .put() which it will receive two params. With it you can either PUT a new value or UPDATE a value.
IDBObjectStore.put(item, key)
item: The item you want to put/update
key: opcional: Your primary object store key (such as an uuid, a random number, in short...) for that item you would like to update.
Code:
//This is an example only.
//Let's think that we have an object store into our IndexDB 'user', where object store is called by user-data:
//# Key Value
//0 1 { username: 'John Doe' }
//Here, we are receiving the 'success' result from an indexedDB.open(), and using its result with a promise.
dbPromise.then(db => {
//Getting the transaction
const transaction = db.transaction('user-data', 'readwrite')
//Getting the objectStore with the data, the same object store before.
const store = transaction.objectStore('user-data')
//Getting the key's object store, in the other other words, this is the key you define when you create you objectStore, with createObjectStore. In this example, I've used 'autoIncrement: true'
const query = store.get(1)
//Getting the query result with a success listener.
query.addEventListener('success', event => {
const { ['result']: user } = event.target
user.productsIntoCart.push(newItem)
//With this, we will be able to change the object store value.
user.username = 'Jane Doe'
store.put(user, 1)
})
query.addEventListener('error', event => console.error(event))
transaction.addEventListener('complete', () => db.close())
})
//# Key Value
//0 1 { username: 'Jane Doe' }
You can see more details you want in the MDN IDBObjectStore.put documentation.
IDBObjectStore

Store multiple channel IDs in a JSON file

I am making a bot using Discord.js and it only needs to track messages in a certain channel, which I currently have this hard-coded for testing purposes.
var { channelID } = require(`./config.json`);
bot.on("message", async (message) => {
const args = message.content.split(/ +/g);
if (message.channel.id === channelID) {
// ...
}
});
I would like for it to store multiple IDs in a JSON file and to have a [p]setchannel command, that would allow me to add one.
I tried this guide, with no luck.
What you probably want to do is store an array of IDs so that you can retrieve them later.
You should have a channelIDs property in your JSON file set to an empty array. Inside your code you can fetch it like this:
const { channelIDs } = require('./config.json') // Now it's an empty array: []
When you want to update this array you should update your local one first, and then you can update the config file: to do that you can use fs.writeFileSync() in combination with JSON.stringify().
const fs = require('fs')
function addChannelID(id) {
channelIDs.push(id) // Push the new ID to the array
let newConfigObj = { // Create the new object...
...require('./config.json'), // ...by taking all the current values...
channelIDs // ...and updating channelIDs
}
// Create the new string for the file so that it's not too difficult to read
let newFileString = JSON.stringify(newConfigObj, null, 2)
fs.writeFileSync('./config.json', newFileString) // Update the file
}
Once you set this function you can add a new ID every time you want, just by calling addChannelID('channel_id').
To check whether the channel the message is coming from should be considered you can use this:
if (channelIDs.includes(message.channel.id)) {
// OK
}

How to create a `context.Provider`/`context.Consumer`-like structure to pass values in a bot app?

I'm trying to pass a property, that is inside the first position of an array of objects, to another module so I can use this value later. I've tried to pass it as module(args), but it keeps reading the default value which is 0. Is there a way to do this?
I tried to implement some React.context but the Bot framework Emulator is refusing it.
/////////////////Module that ll acquire the value/////////////////////////////
getCard(bot, builder, params) {
let configValues = { ...params[0] }
bot.dialog(`${configValues.path}`, function (session) {
var msg = new builder.Message(session);
const cardItem = (obj) => {
return (new builder.HeroCard(session)
.title(`${obj.title}`)
.text(`R$ ${obj.price}`)
.images([builder.CardImage.create(session, `${obj.img}`)])
.buttons([
builder.CardAction.imBack(session, `${obj.price} Item adicionado!`, 'add to cart')
// !onClick event must add the current obj.price to
// the configValues.total(Ex: configValues.total += obj.price)!
])
)
}
msg.attachmentLayout(builder.AttachmentLayout.carousel)
msg.attachments(
eval(params.map(obj => cardItem(obj)))
);
//!in here before end the dialog is where i want to update
// the configValues.total so i can show it in the -> Checkout module
session.send(msg).endDialog()
}).triggerAction({ matches: configValues.regex });
}
}
//////////////CheckOut.Module///////////////////////////////
{...}
let configValues = { ...params[0] }
let state = {
nome: "",
endereco: "",
pagamento: "",
total: configValues.total // this is the value to be read
}
bot.dialog('/intent', [
{...},
(session, results) => {
state.pagamento = results.response
session.send(
JSON.stringify(state) // here is the place to be printed
)
{...}
]
).triggerAction({ matches: /^(finalizar|checar|encerrar|confirmar pedido|terminar)/i })
Since you solved your original problem, I'll answer the one in your comment.
Your problem is here:
cartId.map((obj, i , arr) => {
// if (!obj.total) {
// obj.total.reduce(i => i += i)
// }
const newtotal = new total
newtotal.getTotals(bot, builder, obj, arr)
})
cartId contains the totals for each of your items. When you call map on it, you're passing each item individually to getTotals, which passes each item to checkout()
The reason you can't sum all of the totals and can only sum one item's total is that you pass cartId to checkout and cartId has been changed to just a single item. Instead, there's a couple of different things you could do:
Pass the whole cartId from cartItems and use something like for (var key in cartItems) in totalConstructor() and checkoutConstructor(). This is probably the easiest, but not very memory efficient.
Use BotBuilder's State Storage to store your totals array in userData, then sum that at the end. This might be more difficult to implement, but would be a much better route to go. Here's a sample that can help you get started.

LocalStorage strategy for user and message data

I am looking for a simple strategy to store user data, as well as messages. I was thinking of using different key values like some random token (Ynjk_nkjSNKJN) for users and some real ids (1,2,3) for messages.
Has anyone ever had that problem?
The reason is that I would like to keep localStorage always up to date with new messages from the server, but users should not be deleted during an update.
Thanks
You can handle "tables" in localStorage this way:
//columns should be an array of column literals
function createTable(tableName, columns) {
db[tableName] = {rows: {}, columns: columns};
}
function insertInto(tableName, row, id) {
var newRow = {};
for (var columnName in row) {
if (db[tableName].columns.indexOf(columnName) === -1) {
//invalid column
return false;
}
newRow[columnName] = row[columnName];
}
db[tableName].rows[id] = newRow;
return true;
}
function getIDs(tableName, where) {
var IDs = [];
for (var id in db[tableName].rows) {
if (where(db[tableName].rows[id])) {
IDs[IDs.length]=id;
}
}
return IDs;
}
function update(tableName, where, what) {
what(tableName, getIDs(tableName, where));
}
function deleteRecord(tableName, where) {
var removeIDs = getIDs(tableName, where);
for (var id in removeIDs) {
//Could be done by regexes, but I am not fluent with them and I am lazy to check them out
delete db[tableName].rows[removeIDs[id]];
}
}
function select(tableName, where) {
var IDs = getIDs(tableName, where);
var result = {};
for (var id in db[tableName].rows) {
result[id] = db[tableName].rows[id];
}
return result;
}
function dropTable(tableName) {
delete db[tableName];
}
You probably see that this is only a minimalistic implementation, but with the same approach you can implement altering, joins, grouping and so on. My focus here was just to illustrate how you can create a database. Let's go to the next step, storing the database into localStorage:
localStorage.setItem("db", JSON.stringify(db));
You will need to be able to convert back the local storage item to object, especially because you want to reuse your database even after reload. Let's see how you should initialize db:
var db = !!localStorage.getItem("db") ? angular.fromJson(localStorage.getItem("db")) : {};
Localstorage is a key-value store, which stores everything in string format. So, the messages will be identified by one key (e.g. "messages") and the users another key (e.g. "users").
Then you need to create 2 (angular) services one for the messages and one for the users. Both will interface with localstorage (using the respective keys) and will perform the operations that you want.
If you provide us with more information then we could help you a bit more.

Save Javascript objects in sessionStorage

SessionStorage and LocalStorage allows to save key/value pairs in a web browser. The value must be a string, and save js objects is not trivial.
var user = {'name':'John'};
sessionStorage.setItem('user', user);
var obj = sessionStorage.user; // obj='[object Object]' Not an object
Nowadays, you can avoid this limitation by serializing objects to JSON, and then deserializing them to recover the objects. But the Storage API always pass through the setItem and getItem methods.
sessionStorage.setItem('user', JSON.stringify(user));
var obj = JSON.parse(sessionStorage.getItem('user')); // An object :D
Can I avoid this limitation?
I just want to execute something like this:
sessionStorage.user.name; // 'John'
sessionStorage.user.name = 'Mary';
sessionStorage.user.name // 'Mary'
I have tried the defineGetter and defineSetter methods to intercept the calls but its a tedious job, because I have to define all properties and my target is not to know the future properties.
The solution is to stringify the object before calling setItem on the sessionStorage.
var user = {'name':'John'};
sessionStorage.setItem('user', JSON.stringify(user));
var obj = JSON.parse(sessionStorage.user);
Could you not 'stringify' your object...then use sessionStorage.setItem() to store that string representation of your object...then when you need it sessionStorage.getItem() and then use $.parseJSON() to get it back out?
Working example http://jsfiddle.net/pKXMa/
Either you can use the accessors provided by the Web Storage API or you could write a wrapper/adapter. From your stated issue with defineGetter/defineSetter is sounds like writing a wrapper/adapter is too much work for you.
I honestly don't know what to tell you. Maybe you could reevaluate your opinion of what is a "ridiculous limitation". The Web Storage API is just what it's supposed to be, a key/value store.
This is a dynamic solution which works with all value types including objects :
class Session extends Map {
set(id, value) {
if (typeof value === 'object') value = JSON.stringify(value);
sessionStorage.setItem(id, value);
}
get(id) {
const value = sessionStorage.getItem(id);
try {
return JSON.parse(value);
} catch (e) {
return value;
}
}
}
Then :
const session = new Session();
session.set('name', {first: 'Ahmed', last : 'Toumi'});
session.get('name');
Use case:
sesssionStorage.setObj(1,{date:Date.now(),action:'save firstObject'});
sesssionStorage.setObj(2,{date:Date.now(),action:'save 2nd object'});
//Query first object
sesssionStorage.getObj(1)
//Retrieve date created of 2nd object
new Date(sesssionStorage.getObj(1).date)
API
Storage.prototype.setObj = function(key, obj) {
return this.setItem(key, JSON.stringify(obj))
};
Storage.prototype.getObj = function(key) {
return JSON.parse(this.getItem(key))
};
var user = {'name':'John'};
sessionStorage['user'] = JSON.stringify(user);
console.log(sessionStorage['user']);
Session storage cannot support an arbitrary object because it may contain function literals (read closures) which cannot be reconstructed after a page reload.
You can create 2 wrapper methods for saving and retrieving object from session storage.
function saveSession(obj) {
sessionStorage.setItem("myObj", JSON.stringify(obj));
return true;
}
function getSession() {
var obj = {};
if (typeof sessionStorage.myObj !== "undefined") {
obj = JSON.parse(sessionStorage.myObj);
}
return obj;
}
Use it like this:- Get object, modify some data, and save back.
var obj = getSession();
obj.newProperty = "Prod"
saveSession(obj);
You could also use the store library which performs it for you with crossbrowser ability.
example :
// Store current user
store.set('user', { name:'Marcus' })
// Get current user
store.get('user')
// Remove current user
store.remove('user')
// Clear all keys
store.clearAll()
// Loop over all stored values
store.each(function(value, key) {
console.log(key, '==', value)
})
CustomHook is one of the best ways to store a token as an object by using sessionStorage in React 17.0.1.
Follow these instructions:
Implement sessionStorage get/set inside a custom function:
export default function useToken() {
const getToken = () => {
const tokenString = sessionStorage.getItem('token');
const userToken = JSON.parse(tokenString);
return userToken?.token;
};
const [token, setToken] = useState(getToken());
const saveToken = (userToken) => {
sessionStorage.setItem('token', JSON.stringify(userToken));
setToken(userToken.token);
};
return {
setToken: saveToken,
token,
};
}
This function can be imported and used as a hook in other components like:
import useToken from './useToken';
const { token, setToken} = useToken(); // call CustomHook
const handleSubmit = async (event) => {
event.preventDefault();
// call functionAPI
const token = await loginUser({
username,
password,
});
setToken(token); // AUTOMATICALLY token will be stored to sessionStorage of browser
};

Categories

Resources