React Native undefined is not an object while fetching data - react-native

I want to print a document that uses data that I need to fetch first but it always gets 'undefined is not an object (evaluating 'items.username')' error since the items are still in the fetch state while the HTML variable is created
here's an example of my code
const [items, setItems] = React.useState();
const [loading, setLoading] = React.useState(true)
const getData = async () => {
const response = await axiosInstance.get(`/userData/get`)
if (response?.data?.success) {
const result = response.data?.result[0] || [];
setItems(result);
}
}
React.useEffect(() => {
(async () => {
setLoading(true);
try {
await getData();
} catch (err) {
Alert.alert("", err?.response?.data?.message || err?.message);
}
setLoading(false);
})();
}, []);
const userHTML = `<h1>${items.username}</h1>`
const printHTML = async () => {
await Print.printAsync({
userHTML,
});
};
is there a way to delay the userHTML creation so it will be generated after the fetching is done or maybe there is a best practice to do this kind of thing?
note: the print function will be on the same page as where the userData is being fetched since the data always changed so it will not be possible to fetch data and store it first then move to the page with the print function

You can set the useHTML like this:
const userHTML = `<h1>${!!items ? items.username : ''}</h1>`

Related

Getting Undefined / Null value in async function while using useEffect React Native

Am I doing anything wrong here ?
If I console.log value outside sync function then its getting printed, but inside async function its null
1st time Screen load = no result
2nd time if ctrl + s = getting values
const {un1} = useSelector(state => state.redusername);
const {ps1} = useSelector(state => state.redpassword);
const [user, setUser] = useState([]);
const loadowndetails = async () => {
try {
const resp = await Axios.post(
'https://example.com/owndetailshomepage.php',
{
un: un1,
un: ps1,
},
);
setUser(resp.data.phpresult);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
useEffect(() => {
loadowndetails();
}, []);
1st time Screen load = no result
2nd time if ctrl + s = getting values

Jest does not continue after async method

I have an async method triggered by a click event where I make a call to an API and then process the response, like this:
async confirmName () {
const {name, description} = this.form;
const [data, error] = await Pipelines.createPipeline({name, description});
if (error) {
console.error(error);
this.serviceError = true;
return false;
}
this.idPipelineCreated = data.pipeline_id;
return true;
}
The test looks like this:
test("API success", async () => {
const ConfirmNameBtn = wrapper.find(".form__submit-name");
await ConfirmNameBtn.vm.$emit("click");
const pipelinesApi = new Pipelines();
jest.spyOn(pipelinesApi, "createPipeline").mockResolvedValue({pipeline_id: 100});
const {name, description} = wrapper.vm.form;
pipelinesApi.createPipeline().then(data => {
expect(wrapper.vm.pipelineNameServiceError).toBe(false);
wrapper.setData({
idPipelineCreated: data.pipeline_id
});
expect(wrapper.vm.idPipelineCreated).toBe(data.pipeline_id)
}).catch(() => {})
})
A basic class mock:
export default class Pipelines {
constructor () {}
createPipeline () {}
}
I'm testing a success API call and I mock the API call returning a resolved promised. The problem is the coverage only covers the first two lines of the method, not the part where I assign the response of the API call. Is this the correct approach?
Edit:
Screenshot of coverage report:
Don't mix up await and then/catch. Prefer using await unless you have very special cases (see this answer):
test("API success", async () => {
const ConfirmNameBtn = wrapper.find(".form__submit-name");
await ConfirmNameBtn.vm.$emit("click");
const pipelinesApi = new Pipelines();
jest.spyOn(pipelinesApi, "createPipeline").mockResolvedValue({pipeline_id: 100});
const {name, description} = wrapper.vm.form;
const data = await pipelinesApi.createPipeline();
expect(wrapper.vm.pipelineNameServiceError).toBe(false);
wrapper.setData({
idPipelineCreated: data.pipeline_id
});
expect(wrapper.vm.idPipelineCreated).toBe(data.pipeline_id)
expect(wrapper.vm.serviceError).toBe(false);
})

Reusing async function in react native

I am building a page to fetch data from API when loaded, but encounter waring an effect function must not return anything besides a function which is used for clean-up when trying to reuse the function for fetching data
const dispatch = useDispatch();
useEffect(() => {
// this way does not work as I expected, my page does not show data I fetched
const getData = async () => {
const result = await dispatch(actions.getList());
setState(result);
};
getData();
},[isFirstLoaded]);
But I get the warning when trying below
const dispatch = useDispatch();
const getData = async () => {
const result = await dispatch(actions.getList());
setState(result);
};
useEffect(async() => {
// this way gives me the data but with a warning
await getData();
},[isFirstLoaded]);
How should I reuse the getData function? I did not update the state if I am not using the async and await here. When I use async and await here, I get the warning. Thanks.
overall, you are heading in the right direction. For fetching data, you'd wanna use use Effect and pass [] as a second argument to make sure it fires only on initial mount.
I believe you could benefit from decoupling fetching function and making it more generic, as such:
const fetchJson = async (url) => {
const response = await fetch(url);
return response.json();
};
const Fetch = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetchJson("https://api.coindesk.com/v1/bpi/currentprice.json")
.then(({ disclaimer }) => setData(disclaimer));
}, []);
return <Text>{data}</Text>;
};

How to outsource asyncData to Vuex Store?

I'm currently loading some data from firebase I wan't to be server side rendered so it can be indexed for SEO in asyncData on a page.
asyncData() {
return firebase.firestore().collection('Programms').get().then((querySnapshot) => {
const programms = [];
querySnapshot.forEach((doc) => {
const programm = doc.data();
programm.id = doc.id;
programms.push(programm)
})
return { programms: programms};
})
However I would like to convert this to my vuex store.
I know I could do this:
const actions = {
async nuxtServerInit({ commit }) {
firebase.firestore().collection('Programms').onSnapshot((querySnapshot) => {
const programms = [];
querySnapshot.forEach((doc) => {
const programm = doc.data();
programm.id = doc.id;
programms.push(programm)
})
console.log('loaded Programms', programms)
commit('setProgramms', programms);
})
},
}
But this way the data will be loaded for every route in my app. I wan't to load this data only in some pages where I also display it, so I don't load it unnecessary.
How could I do this in Vuex?
As #aldarund says, the fetch method is precisely what you want.
fetch ({ store, params }) {
return firebase.firestore().collection('Programms').get().then((querySnapshot) => {
const programms = [];
querySnapshot.forEach((doc) => {
const programm = doc.data();
programm.id = doc.id;
programms.push(programm)
})
.then(() => {
store.commit('setPrograms', programms)
})
}
See the docs here.

React Native - get data from Promise

in my React Native app I receive a token from an API. Everytime the app sends a request to the server this token is needed. I save the token in the AsyncStorage:
export const onSignIn = (value) => AsyncStorage.setItem('USER_TOKEN', value);
In many different parts of the app I need this token and therefore I wanted to use a function, that extracts the information out of the token:
export const getTokenInfo = async () => {
try{
const value = await AsyncStorage.getItem('USER_TOKEN')
.then((res) => {
const jsonData = jwtDecode(res);
return jsonData;
})
}
catch(e){
console.log('caught error', e);
}
}
When calling the function in other Components it just returns the Promise itself and not the token. Is there a possibility to get the token, but not the promise? A possible approach was to use setState() to store the token in a state, but there are some components like DrawerNavigator that are not in a class.
Thanks!
Your forgot to return the value on your getTokeninfo function
export const getTokenInfo = async () => {
try{
const value = await AsyncStorage.getItem('USER_TOKEN')
.then((res) => {
const jsonData = jwtDecode(res);
return jsonData;
})
return value // <---- you forgot this line
}
catch(e){
console.log('caught error', e);
}
}