react native lazy initial state from asyncstorage - reactjs

I try to get a string from asyncStorage and set it as initial state in a functional component.
I found in the documentation that we can provide a function like initial state. I tried some solutions with asyncStorage and sqlite and don't get results. This is some code, What is wrong and is there some other solution for storing active theme in storage.
const retrieveData = async () => {
try {
const value = await AsyncStorage.getItem("theme");
return value;
} catch (error) {}
};
const [isTheme, setIsTheme] = useState(retrieveData);
useEffect(() => {
AsyncStorage.setItem("theme", isTheme);
}, [isTheme]);

I think retrieveData is still a promise. You should get the value by doing await(retrieveData)

Related

React Native I can not store an array with AsyncStorage

I am newbie in React Native and I am trying to store and get an array with AsyncStorage in ReactNative.
I have two problems.
First, I do not know why but when I storage data, it only works the second time but I am calling first the set of useState.
const handleAddTask = () => {
Keyboard.dismiss();
setTaskItems([...taskItems, task]);
storeData(taskItems);
};
Second, how can I call the getData function to get all the data and show it? Are there something like .onInit, .onInitialize... in ReactNative? Here is my full code
const [task, setTask] = useState();
const [taskItems, setTaskItems] = useState([]);
const handleAddTask = () => {
Keyboard.dismiss();
setTaskItems([...taskItems, task]);
storeData(taskItems);
};
const completeTask = (index) => {
var itemsCopy = [...taskItems];
itemsCopy.splice(index, 1);
setTaskItems(itemsCopy);
storeData(taskItems);
}
const storeData = async (value) => {
try {
await AsyncStorage.setItem('#tasks', JSON.stringify(value))
console.log('store', JSON.stringify(taskItems));
} catch (e) {
console.log('error');
}
}
const getData = async () => {
try {
const value = await AsyncStorage.getItem('#tasks')
if(value !== null) {
console.log('get', JSON.parse(value));
}
} catch(e) {
console.log('error get');
}
}
Updating state in React is not super intuitive. It's not asynchronous, and can't be awaited. However, it's not done immediately, either - it gets put into a queue which React optimizes according to its own spec.
That's why BYIRINGIRO Emmanuel's answer is correct, and is the easiest way to work with state inside functions. If you have a state update you need to pass to more than one place, set it to a variable inside your function, and use that.
If you need to react to state updates inside your component, use the useEffect hook, and add the state variable to its dependency array. The function in your useEffect will then run whenever the state variable changes.
Even if you're update state setTaskItems([...taskItems, task]) before save new data in local storage, storeData(taskItems) executed before state updated and save old state data.
Refactor handleAddTask as below.
const handleAddTask = () => {
Keyboard.dismiss();
const newTaskItems = [...taskItems, task]
setTaskItems(newTaskItems);
storeData(newTaskItems);
};

Return data from Async function React Native Redux

I am having trouble with accessing the data after fetching it with SecureStore in Expo for react-native.
Here is the simple code:
const infoofuser = SecureStore.getItemAsync('userInfo').then(value =>
console.log(`this is the vlaue from infouser: ${value}`),
);
console.log(`infoouser: ${JSON.stringify(infoofuser)}`);
the first infoofuser constant definition returns the object of the intended data.
console.log(`infoouser: ${JSON.stringify(infoofuser)}`);
however returns {"_U":0,"_V":0,"_W":null,"_X":null} which U understand is a promise. I would like to simply get the data that comes from the SecureStore call and use it to set my initialState in redux.
const infoofuser = SecureStore.getItemAsync('userInfo').then(value =>
value
);
this does not work either to access the data
You can use async method using async/await. Try this:
const userInfo = useSelector(state => state.userInfo);
const getData = async () => {
try {
const infoofuser = await SecureStore.getItemAsync('userInfo');
console.log('infoofuser:', infoofuser)
/// strore on redux
} catch (err) {
// handle error
}
}
useEffect(() => {
getData()
}, [])
if (!userInfo) return null
//render something else
You can check the Expo Secure Store docs for reference.

Ho to wait with fetch until redux state is available?

I want to fetch some data from a database, and depending on the user the returned data should differ. The way i tried it, was by passing the userid as a query. The id is stored as a redux state. The problem is, that it takes some time before the redux state is available. Ive tried fixing this with if statements, and rerunning the useEffect everytime the auth state is updated. This doesn't work.
I want to fetch, when the redux state auth.user.id is available. Which it is like .1 sec after the initial load.
Here is my code:
const auth = useSelector((state) => state.auth);
useEffect(async () => {
if (auth.token.length > 0) {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
}
}, [auth, date]);
I believe useEffect is already asynchronous, so you don't need to use the async keyword in the anonymous callback. You can create the async function for that logic elsewhere and call it within the useEffect.
Similarly, you could put in self calling async function within your useEffect as such:
useEffect(() => {
(async () => {
if (auth.token.length) {
try {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
}catch (err) {console.log(err);}
}
})();
}, [auth, date]);
I think this link may be helpful:
React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing
So with the basic understanding, I assume that you need to call the API whenever userId is available. try the below useEffect
useEffect(async () => {
// check user id is available here
if (auth.user && auth.user.id) {
const res = await getData(`calendar/?userId=${auth.user.id}`);
setLessons(res.lessons);
setEvents(res.events);
// some other statements
}
}, [auth, date]);

How to set default value and if not then the value from AsyncStorage in react Native

I am new to React Native, so trying to learn.
I am trying to set the default value using hooks in react native as below
const [recent, setRecent] = useState('No data')
I am using the async storage to get the value of recent. If the value is not found in async storage then I need to set the default value as No Data
How can I achieve this behaviour?
You'll need to use the useEffect hook. It will run when the component is first mounted. In it you need to run an async function to check AsyncStorage for the value. That has to be done in a specific way, which I've shown in the example below (create async function, then immediately run it). Then update the value based on what it finds.
const [ recent,setRecent ] = React.useState()
React.useEffect(() => {
const checkAsync = async () => {
const value = await AsyncStorage.getItem('#storage_Key')
if (value !== undefined && value !== null){
setRecent(value)
} else {
setRecent('No data')
}
}
checkAsync()
}, [])

react promise in functional component with UseEffect and UseState doesn't work

I'm having issue fetching data and setting them to state in a functional component using useEffect and useState.
My problem is that I would like to keep the data fetching done with axios async/await in a separate file for improving application scalability but then I don't understand how to update the state in case the promise is resolved (not rejected).
In particular I'm trying to retrieve from the promise an array of table rows called data in state, but I can't figure out how to set the result of the responce in the state
Here's the code in the component file:
const [data, setData] = React.useState([]);
useEffect(() => {
const { id } = props.match.params;
props.getTableRows(id).then((res) => {
setData(res);
});
//or is it better:
//props.getTableRows(id).then(setData); ?
}, []);
and my action.js:
export const getTableRows = (id, history) => async (dispatch) => {
try {
const res = await axios.get(`/api/test/${id}`);
dispatch({
type: GET_TEST,
payload: res.data.rows,
});
} catch (error) {
history.push("/test");
}
};
In the above picture it can be seen that the rows array inside the promise response called in action.js is present.
This code unfortunately doesn't work, error: Uncaught (in promise) TypeError: Cannot read property 'forEach' of undefined
I've found out another solution which is the define the promise in the useEffect method like this:
useEffect(() => {
const { id } = props.match.params;
const fetchData = async () => {
const result = await axios.get(`/api/test/${id}`);
setData(result.data.rows);
};
fetchData();
}, []);
this code is working in my app but as I said I don't like having the promises in the components files I would like instead to have them all the promise in action.js for app scalability (in case url change I don't have to change all files) but in that case I don't know where to put the setData(result.data.rows); which seems the right choise in this last example
Any suggestions?
Thanks
You still need to use async/await. The .then() is executed when the value is returned, however your function will continue rendering and won't wait for it. (causing it to error our by trying to access forEach on a null state). After it errors the promise via .then() will update the values and that is why you can see them in the console.
useEffect(() => {
async function getData() {
const { id } = props.match.params;
await props.getTableRows(id).then((res) => {
setData(res);
});
}
getData()
}, []);
Additionally, before you access a state you can check for null values (good practice in general).
if (this.state.somestate != null) {
//Run code using this.state.somestate
}
I don't see you return anything from getTableRows. You just dispatch the result, but hadn't return the res for the function call.
And it will be helpful if you provided error trace.

Resources