Getting items from localstorage returns null - javascript

I have a React app that loads items from a api call from a database, if there is no data in local storage. I will then set the state with data and load the data in local storage. Next time the app loads it will take the data from local storage instead. The data is mapped out in the return statement.
The Problem is it still returns null even when there is data in local storage.
I will load the data from componentDidMount:
The below code will run the function "loadItems" that first check if there is any data in localstorage (name "workItems") and if so, store it in the state. If it isn't, it will call the database in an api call, store the data to state and also store the data in localstorage which will be used next time the component mounts. I have confirmed that the data is stored in the browser. But when the data from the localstorage exist, is mapped to the state and finally mapped out in the return from the render function it will complain the data is "null". How come? The data is stored in the local storage and exist there when I inspect it from the dev tools.
componentDidMount() {
this.loadItems(false);
}
async loadItems(forcedUpdate) {
const payload = {
forcedUpdate: forcedUpdate
};
if (localStorage.getItem('workItems').length > 0) {
let data = localStorage.getItem("workItems");
this.setState({
workitems: localStorage.getItem("workItems"),
loading: false,
sources: allSources,
showSources: allSources,
}, () => {
return;
});
}
var apiUrl = "api/WorkItem/GetWorkItems";
const response = await Axios.default.post(apiUrl, payload);
console.log(response);
var allSources = response.data.items
.map(item => item.source)
.filter((value, index, self) => self.indexOf(value) === index);
this.setState({
workitems: response.data.items,
loading: false,
sources: allSources,
showSources: allSources,
failedScrape: response.data.failedscrape,
lastUpdated: response.data.lastUpdated
});
localStorage.setItem('workItems', response.data.items);
}

localStorage.setItem(key, value) expecting a value to be string. When you pass the object such as [{id: 1}], it will typecast it to string. Hence the object becomes the string like this "[object Object]".
localStorage.setItem('test', [{id: 1}]);
const item = localStorage.getItem('test');
console.log(item) // "[object Object]" a string
console.log(item[0]) // "["
console.log(item[1)) // "o"
Solution
The solution is to stringify it before saving to localStrage and parse it after getting the item.
localStorage.setItem('workItems', JSON.stringify(response.data.items));
// when you get the items
if (JSON.parse(localStorage.getItem('workItems')).length > 0)

You can use this hook. Otherwise, to fetch data from local storage, use these functions:
const getValue = (key, defaultValue = {}) => {
try {
// read value from local storage
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.log(error);
return defaultValue;
}
}
const setValue = (key, value) => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.log(error);
}
}
setValue("test", {test: "Test value"})
console.log(getValue("test"))

Related

Why is local storage deleted when I refresh the page?

I have a reducer for state management at the context API. I was saving my Todos and it's happening successfully but when ı refresh the page all todos is deleting and just stay empty array.
// The relevant part in the reducer.
case "TODO_ADD_USER":
return {
...state,
users: action.payload,
};
// Localstorage functions.
useEffect(() => {
saveLocalTodos();
}, [state]);
useEffect(() => {
const localTodos = getLocalTodos();
dispatch({ type: "TODO_ADD_USER", payload: localTodos });
}, []);
const saveLocalTodos = () => {
if (localStorage.getItem("users") === null) {
localStorage.setItem("users", JSON.stringify([]));
} else {
localStorage.setItem("users", JSON.stringify(state.users));
}
};
const getLocalTodos = () => {
let locals;
if (localStorage.getItem("users") === null) {
locals = [];
} else {
locals = JSON.parse(localStorage.getItem("users"));
}
return locals;
};
Place of keeping the state.
const users = {
users: [],
};
There are a couple issues with your code.
The biggest one here is that you are saving the todos before getting them. So at the start of the application, things are getting reset, which is problematic.
Up next, you have your condition for the saving a bit backwards. You want to check if state.users === null and do a special action for that, rather than if localStorage.getItem("users") === null, as that will be null by default, and have nothing to do with the value in memory.
In fact, if the localStorage value is not null, but the state.users is, then it would set "null" to localStorage, which is less than ideal.
Here's the working code:
useEffect(() => {
// Get the item from local storage. JSON.parse(null) returns null rather than throws
// Get from local storage before setting it
const localTodos = JSON.parse(localStorage.getItem("users")) || [];
dispatch({ type: "TODO_ADD_USER", payload: localTodos });
}, []);
useEffect(() => {
// The conditions for this was wrong.
// You want to check if `state.users` has a value
// If it does, store! If not, don't store.
// That way you don't reset data
// In the case that you have this app running in two windows,
// there's more that needs to be done for that.
if (state.users) {
localStorage.setItem("users", JSON.stringify(state.users || []));
}
}, [state]);
https://codesandbox.io/s/angry-glitter-9l10t?file=/src/App.js

Data Not updated when fetch query - React-Query?

I have a three-check box type,
When I check any box I call refetch() in useEffect().
The first time, I check all boxes and that returns the expected data!
but for some cases "rechange the checkboxes randomly", the returned data from API is "undefined" although it returns the expected data in Postman!
So I Guess should I need to provide a unique queryKey for every data that I want to fetch
so I provide a random value "Date.now()" but still return undefined
Code snippet
type bodyQuery = {
product_id: number;
values: {};
};
const [fetch, setFetch] = useState<number>();
const [bodyQuery, setBodyQuery] = useState<bodyQuery>({
product_id: item.id,
values: {},
});
const {
data: updatedPrice,
status,
isFetching: loadingPrice,
refetch,
} = useQuery(
['getUpdatedPrice', fetch, bodyQuery],
() => getOptionsPrice(bodyQuery),
{
enabled: false,
},
);
console.log('#bodyQuery: ', bodyQuery);
console.log('#status: ', status);
console.log('#updatedPrice: ', updatedPrice);
useEffect(() => {
if (Object.keys(bodyQuery.values).length > 0) {
refetch();
}
}, [bodyQuery, refetch]);
export const getOptionsPrice = async (body: object) => {
try {
let response = await API.post('/filter/product/price', body);
return response.data?.detail?.price;
} catch (error) {
throw new Error(error);
}
};
So after some elaboration in the chat, this problem can be solved by leveraging the useQuery key array.
Since it behaves like the dependency array in the useEffect for example, everything that defines the resulted data should be inserted into it. Instead of triggering refetch to update the data.
Here the key could look like this: ['getUpdatedPrice', item.id, ...Object.keys(bodyQuery.values)], which will trigger a new fetch if those values change and on initial render.

How to push multiple items into an array and load into async storage in react native?

I am attempting to push data into local storage in react native, in this case push multiple elements. I am attempting to use documentation pointed out here:
How do I set multiple values within Asyncstorage
How would I go about doing this properly? Below is some code:
What I am currently doing
const STORAGE_KEY = '#save_enableauto';
const DBLTIME_KEY = '#save_dbltime';
state={
times: Times,
messageTimes: {
dblTime: '12:00 pm',
genTime: '12:00 pm'
}
enableAuto:false
}
//retrieves automatic messaging status
_retrieveData = async () => {
try {
//pull data from local storage
const enableAuto = await AsyncStorage.getItem(STORAGE_KEY);
const dblTime = await AsyncStorage.getItem(DBLTIME_KEY);
console.log('auto messages set: ',enableAuto);
console.log('time data is:', dblTime);
//reset state for time if it exists in local storage
if(dblTime !==null) {
this.setState(prevState => ({
messageTimes: { // object that we want to update
...prevState.messageTimes, // keep all other key-value pairs
dblTime: dblTime // update the value of specific key
}
}))
}
//reset state for notifications if exists in local storage
if (enableAuto !== null) {
// We have data!!
console.log('receiving from local storage: ',enableAuto);
this.setState({ enableAuto:eval(enableAuto) });
}
} catch (error) {
alert('failed to load previous settings.')
// Error retrieving data
}
};
//trying to set it up with one call
_retrieveDataGroup = async () => {
const items = JSON.stringify([['k1', STORAGE_KEY], ['k2', DBLTIME_KEY]]);
try {
const localData = AsyncStorage.multiGet(items, () => {
//to do something
});
console.log('Group fetch: ',localData);
} catch (error) {
alert('failed to load previous settings.')
// Error retrieving data
}
};
right now what I receive when console logging group fetching is a promise:
Group fetch: Promise {
"_40": 0,
"_55": null,
"_65": 0,
"_72": null,
}
multiGet is a Promise. Add await before calling it.
const localData = await AsyncStorage.multiGet(items, () => {
//to do something
});

Vue, Vuex, JavaScript: includes() does not work as expected

I wanna store some objects inside an array if the array doesn't already contain some object with the same id. anyways, everything works fine til i start adding more than one object at a time.
Here is the related code using Vuex:
// filter function to check if element is already included
function checkForDuplicate(val) {
for( let sessionItem of state.sessionExercises ) {
return sessionItem._id.includes(val._id);
}
};
// related array from vuex state.js
sessionExercises: [],
// vuex mutation to store exercises to session exercises
storeSessionExercises: (state, payload) => {
// Pre filtering exercises and prevent duplicated content
if( checkForDuplicate(payload) === true ) {
console.log("Exercise ist bereits für session registriert!");
} else {
state.sessionExercises.push(payload);
}
},
// Related vuex action
storeSessionExercises: ({ commit }, payload) => {
commit("storeSessionExercises", payload)
},
As I wrote before everything works fine as long i ad a single object, checkForDuplicate() will find duplicated objects and deny a push to the array.
now there is a case in which I wanna push a bundle of objects to the array, which i am doing through an database request, looping through the output, extracting the objects and pushing them through the same function as I do with the single objects:
// get user related exercises from database + clear vuex storage + push db-data into vuex storage
addSessionWorkout: ({ commit, dispatch }, payload) => {
axios.post(payload.apiURL + "/exercises/workout", payload.data, { headers: { Authorization: "Bearer " + payload.token } })
.then((result) => {
// loop through output array and
for( let exercise of result.data.exercises ) {
// push (unshift) new exercise creation to userExercises array of vuex storage
dispatch("storeSessionExercises", exercise)
};
})
.catch((error) => {
console.error(error)
});
},
The push does also work as it should, the "filter function" on the other hand doesn't do its job. It will filter the first object and deny to push it to the array, but if there is a second one that one will be pushed to the array even inf the same object (same Id) is already included, what am I not seeing here!? makes me nuts! :D
I understand it like the loop will put each object through the checkForDuplicate() and look if there is an duplicate it should output true, so the object doesn't get pushed into the array. If anybody sees what I currently don't just let me know.
the mistake is your filter function. you want to loop over your sessionExercises and only return true if any of them matches. However, at the moment you return the result of the very first check. Your loop will always only run one single time.
Option 1: only return if matched
function checkForDuplicate(val) {
for( let sessionItem of state.sessionExercises ) {
if (sessionItem._id.includes(val._id)) {
return true;
}
}
return false;
};
Option 2: use es6 filter
storeSessionExercises: (state, payload) => {
var exercises = state.sessionExercises.filter(ex => (ex._id.includes(payload._id)));
if(exercises.length) {
console.log("Exercise ist bereits für session registriert!");
} else {
state.sessionExercises.push(payload);
}
}
I would change the addSessionWorkout action, I would create a new exercises array with the old and new entries and then update the state.
// related array from vuex state.js
sessionExercises: [],
// vuex mutation to store exercises to session exercises
storeSessionExercises: (state, payload) => {
state.sessionExercises = payload;
},
// Related vuex action
storeSessionExercises: ({ commit }, payload) => {
commit("storeSessionExercises", payload)
},
addSessionWorkout: async({
commit,
dispatch,
state
}, payload) => {
const result = await axios.post(payload.apiURL + "/exercises/workout", payload.data, {
headers: {
Authorization: "Bearer " + payload.token
}
})
try {
const newExercices = result.data.exercises.reduce((acc, nextItem) => {
const foundExcercise = acc.find(session => session.id === nextItem.id)
if (!foundExcercise) {
return [...acc, nextItem]
}
return acc
}, state.sessionExercises)
dispatch("storeSessionExercises", foundExcercise)
} catch (e) {
console.error(error)
}
},

How to store and retrieve multiple data using AsyncStorage

I'm new to ReactNative, But I have a bit experience in React.
Here I'm trying to store multiple data in AsyncStorage and trying to retrieve them, But I'm only able to store single data
Code:
state = {
data: [],
item: ""
};
storeData = async () => {
await AsyncStorage.setItem("#storage_Key", JSON.stringify(this.state.item));
this.getData();
};
componentDidMount() {
this.getData();
}
getData = async () => {
try {
const value = await AsyncStorage.getItem("#storage_Key");
let { data, item } = this.state;
data.push(value);
this.setState({
data: data,
item: ""
});
} catch (e) {
// error reading value
}
};
Any help or guidance would be appreciated
You are only setting and retrieving one item when you use setItem and getItem respectively.
If you want to store multiple items, you can use multiSet
AsyncStorage.multiSet(['key1', 'value1'], ['key2', 'value2']);
If you want to retrieve multiple items, you can use multiGet
AsyncStorage.multiGet(['key1', 'key2'], (err, items) => {
console.log({ items });
});
I have good experience using
react-native-storage
from what i see from your code you are saving state's item
when you retrieve your data from storage you are putting it in data, is that right?
I suggest you to change the way you arrange the data
you use let { data } = this.state;
if you know you can't set state's data using =
i suggest you to make new variable -> let data and store it with data = value then you can setState({ data: data })

Categories

Resources