How to synchronous useState with passing state to localstorage - reactjs

I ran into an asynchronous useState problem.
I have a situation where I first need to add an object to the state array in the handler. And then add this state to the localStorage.
setFavoritedSongs ((prev) => [...prev, {name: "Lala", length: "3:20"}]);
localStorage.setItem("storageItemName", JSON.stringify(favoritedSongs));
If I delete the entire localStorage first and run the handler. So an empty array is added to my localStorage (the state shows me updated). After the action again, the required object is finally added to my array.
I tried something like this, but still the same problem.
const tempArray = favoritedSongs.push({ name: "Lala", length: "3:20" });
localStorage.setItem(storageItemName, JSON.stringify(tempArray));
How do you solve this, please?
/// EDIT
I have something like this
const FavoriteSong = () => {
const song = { id: 1, name: "Lala", length: "3:20" };
const [favoritedSongs, setFavoritedSongs] = useState([]);
const [isFavorited, setIsFavorited] = useState(false);
useEffect(() => {
if (localStorage.getItem("storageItemName")) {
const storageSongs = JSON.parse(
localStorage.getItem("storageItemName") || ""
);
setFavoritedSongs(storageSongs);
const foundSong = storageSongs?.find((song) => song.id === song.id);
foundSong ? setIsFavorited(true) : setIsFavorited(false);
}
}, [song]);
const handleClick = () => {
if (isFavorited) {
const filteredSong = favoritedSongs.filter(
(song) => song.id !== song.id
);
localStorage.setItem("storageItemName", JSON.stringify(filteredSong));
setIsFavorited(false);
} else {
setFavoritedSongs((prev) => [...prev, song]);
localStorage.setItem("storageItemName", JSON.stringify(favoritedSongs));
setIsFavorited(true);
}
};
return <div onClick={handleClick}>CLICK</div>;
};
export default FavoriteSong;

Just place your localStorage.set logic inside a useEffect to make sure it runs after the state actually changes.
useEffect() => {
localStorage.setItem(...);
}, [favoritedSongs]};

For that you can Use the condition If data in the array then It will set in localStorage otherwise not
const tempArray = favoritedSongs.push({ name: "Lala", length: "3:20" });
tempArray.length && localStorage.setItem(storageItemName, JSON.stringify(tempArray));
.
setFavoritedSongs ((prev) => [...prev, {name: "Lala", length: "3:20"}]);
FavoritedSongs.length(your state name) && localStorage.setItem("storageItemName", JSON.stringify(favoritedSongs));

Related

Is it valid, to update the state by mapping to new Objects

Let's take this Update State example:
const initialState = [
{id: 1, country: 'Austria'},
{id: 2, country: 'Belgium'},
{id: 3, country: 'Canada'},
];
const [data, setData] = useState(initialState);
const updateState = () => {
setData(prevState => {
const newState = prevState.map(obj => {
if (obj.id === 2) {
return {...obj, country: 'Denmark'};
}
return obj;
});
return newState;
});
};
1. Is it also valid to update the state like this? (First example)
const updateState = () => {
const newState = data.map(obj => {
if (obj.id === 2) {
return {...obj, country: 'Denmark'};
}
return obj;
});
setData(newState);
};
2. Is it also valid to update the state like this? (Second example)
const updateState = () => {
setData(prevState => {
const newState = prevState.map(obj => {
if (obj.id === 2) {
let newObj = obj;
newObj.country = 'Denmark'
return newObj;
}
return obj;
});
return newState;
});
};
3. Do this specific versions also have performance impacts? Which one is the best?
The first and the second example are perfectly valid. I would, however, suggest you to use the first one and I will explain why:
With the first example you are using a callback as an argument of the function. And this form means that you are actually getting the last data state value (this is important because the state updates happen asynchronously). Whenever you need to update the state based on the previous value even React suggests to use the callback form to avoid side effects.
More infos here: https://reactjs.org/docs/hooks-reference.html#functional-updates
The third example is not valid because you are mutating directly the state. Something that in react is not allowed.
More infos here: https://dev.to/il3ven/common-error-accidentally-mutating-state-in-react-4ndg

Too many re-renders with useSelector hook closure

Considering this state, I need to select some data from it:
const initialState: PlacesStateT = {
activeTicket: null,
routes: {
departure: {
carriageType: 'idle',
extras: {
wifi_price: 0,
linens_price: 0,
},
},
arrival: {
carriageType: 'idle',
extras: {
wifi_price: 0,
linens_price: 0,
},
},
},
};
so, I came up with two approaches:
first:
const useCoaches = (dir: string) => {
const name = mapDirToRoot(dir);
const carType = useAppSelector((state) => state.places.routes[name].carriageType);
const infoT = useAppSelector((state) => {
return state.places.activeTicket.trainsInfo.find((info) => {
return info.routeName === name;
});
});
const coaches = infoT.trainInfo.seatsTrainInfo.filter((coach) => {
return coach.coach.class_type === carType;
});
return coaches;
};
and second:
const handlerActiveCoaches = (name: string) => (state: RootState) => {
const { carriageType } = state.places.routes[name];
const { activeTicket } = state.places;
const trainInfo = activeTicket.trainsInfo.find((info) => {
return info.routeName === name;
});
return trainInfo.trainInfo.seatsTrainInfo.filter((coach) => {
return coach.coach.class_type === carriageType;
});
};
const useActiveInfo = (dir: string) => {
const routeName = mapDirToRoot(dir);
const selectActiveCoaches = handlerActiveCoaches(routeName);
const coaches = useAppSelector(selectActiveCoaches);
return coaches;
};
Eventually, if the first one works ok then the second one gives a lot of useless re-renders in component. I suspect that there are problems with selectActiveCoaches closure, maybe react considers that this selector is different on every re-render but I am wrong maybe. Could you explain how does it work?
selectActiveCoaches finishes with return seatsTrainInfo.filter(). This always returns a new array reference, and useSelector will force your component to re-render whenever your selector returns a different reference than last time. So, you are forcing your component to re-render after every dispatched action:
https://react-redux.js.org/api/hooks#equality-comparisons-and-updates
One option here would be to rewrite this as a memoized selector with Reselect:
https://redux.js.org/usage/deriving-data-selectors

Why useSelector is not selecting redux state

(state) => state?.clientAddress?.getAddress
);
useEffect(() => {
dispatch(GET_CLIENT_ADDRESS({}))
}, [dispatch]);
const onSavedAddressSelect = (addressId) => {
debugger
if (addressId) {
const { data: selectedAddress = [] } = clientAddress ?? {} ;
console.clear()
console.log(selectedAddress)
const addressObj = selectedAddress?.filter(
(addressItem) => addressItem?.value === addressId
)?.[0];
setAddress(addressObj);
setSelectedVenue(addressObj?.value);
}
props.drawer.setDrawer({
open: false,
});
};
My clientAddress inside onSavedAddressSelect is not getting updated with latest redux state even if I dispatch on first render on useEffect.
Please help.
isn't clientAddress is not updated because you call
dispatch(GET_CLIENT_ADDRESS({})) with empty object, so it's stays the same? Try to pass there some object with values

how to set state array with react hooks

Thanks In Advance.
Am new to react hooks (REact Native)
Iam maintaining 2 state array, 1 has the list of item to be displayed and another one is the list user selects from 1st array.
const [myList, setMyList] = React.useState([
{
ID: 0,
AddName: 'Zion 1'
},
{
ID: 1,
AddName: 'Zion 2',
},
{
ID: 2,
AddName: 'Zion 3',
}
])
const [userSelectedList, setUserSelectedList] = React.useState([])
How do i have a function for a flatlist render onclick, if user selected 1 item it should get in to the state userSelectedList and if user click that again the item should be removed from userSelectedList.
Am not keeping my code here as its so shaggy. Can anyone help me out with the exact login and syntax to work with.
const itemOnClick =(item) =>{
//logic --setState
}
State mutation is a problem here.
const [userSelectedList, setUserSelectedList] = React.useState(null)
const itemOnClick =(item) =>{
item !== null ? setUserSelectedList(item) :setUserSelectedList(null)
}
but if you are using select element I preferred to make a react reference.
const [userSelectedList, setUserSelectedList] = React.useState([]);
const itemOnClick =(item) => {
if (userSelectedList.length === 0) {
setUserSelectedList(item)
return;
}
const arrId = myList.map(item => item.id);
if(arrId.includes(item.id)) {
const newArrUserSelected = userSelectedList.filter(val => val.id !== item.id)
setUserSelectedList(newArrUserSelected)
} else {
setUserSelectedList([...userSelectedList, item])
}
}
Hope to help you!

update store on mobx react hook asynchronously

I am trying to fetch the data asynchronously from firestore to my local state using react hooks and mobx and got no clue how to update the store after getting data from firestore. I have used https://github.com/IjzerenHein/firestorter/blob/master/docs/API.md#Collection+query
My store
const store = useObservable({
forms: [],
async initForms(user_id) {
console.log(user_id);
my_forms.query = ref =>
ref
.where('userId', '==', user_id)
.limit(20);
let obj = {};
const arr = [];
my_forms.docs.forEach((doc, i) => {
obj = {
key: i + 1,
title: doc.data.title,
id: doc.id,
tags: doc.data.tags,
category: doc.data.category,
locked: doc.data.locked
};
arr.push(obj);
});
//i can see records from firestore
console.log('arr',arr);
return arr;
},
})
How i tried to update store
useEffect(() => autorun(() => {
store.forms=store.initForms(user_id);
}),[]);
store.forms && (
store.forms.length > 0 ? (
<FuseAnimateGroup
enter={{
animation: "transition.slideUpBigIn"
}}
className="flex flex-wrap py-24"
>
{store.forms.map((f) => {
Well i fixed it by using classic mobx store and using them as the context
These are the steps i did
Create the classic mobx store like we used to do in normal react project
export class MyFormsStore {
forms: []
async initForms(user_id) {
console.log(user_id);
my_forms.query = ref =>
ref
.where('userId', '==', user_id)
.limit(20);
await my_forms.fetch();
let obj = {};
const arr = [];
my_forms.docs.forEach((doc, i) => {
obj = {
key: i + 1,
title: doc.data.title,
id: doc.id,
tags: doc.data.tags,
category: doc.data.category,
locked: doc.data.locked
};
arr.push(obj);
});
this.forms = arr;
}
}
decorate(MyFormsStore, {
forms: observable
})
Export it as the context
export default createContext(new MyFormsStore());
Play with store as the functional component using useContext hook
const Forms = observer((props) => {
const store = useContext(MyFormsStore)
.
.
.
Fetch the data from firestore using useEffect hook
useEffect(() => {
store.initForms(user_id);
}, []);
Check if the store has been initialised or not , it yes render it
{
store.forms !== undefined && (
store.forms.length > 0 ? (
//render
I am still looking how can I memoize expensive functions so that I can avoid calling them on every render using useMemo . Feedbacks will be appreciated
You don't need the useEffect to update data that is tracked by Mobx.
The simplest way is to use runInAction from MobX.
So when your async function is finally resolved, you then use runInAction to update the store. And that will eventually trigger the rendering of react components that are using that particular data.
something like this:
async fetchProjects() {
this.githubProjects = []
this.state = "pending"
try {
const projects = await fetchGithubProjectsSomehow()
const filteredProjects = somePreprocessing(projects)
// after await, modifying state again, needs an actions:
runInAction(() => {
this.state = "done"
this.githubProjects = filteredProjects
})
} catch (error) {
runInAction(() => {
this.state = "error"
})
}
}
You can read more in official documentation:
Writing asynchronous actions

Resources