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>`
My React Native app uses axios to connect to a backend. This is done in the file myApi.js:
class client {
axiosClient = axios.create({
baseURL: example.com,
});
async get(url, data, config) {
return this.axiosClient.get(url, data, config);
}
async put(url, data, config) {
return this.axiosClient.put(url, data, config);
}
async post(url, data, config) {
return this.axiosClient.post(url, data, config);
}
}
export default new client();
I have a component which contains a useEffect which is controlled by a date picker. The selected date is held in a context called DateContext. When the selected date changes, a request is fired off to get some data for that date. When data is returned, it is displayed to the user.
The Component is:
const DaySelect = () => {
const {dateState} = useContext(DateContext);
useEffect(() => {
const load = () => {
const url = '/getDataForDate';
const req = {
selectedDate: moment(dateState.currentDate).format(
'YYYY-MM-DD',
),
};
myApi
.post(url, req)
.then((res) => {
// Now put results into state so they will be displayed
})
};
load();
}, [dateState.currentDate]);
return (
<View>
<>
<Text>Please select a date.</Text>
<DateSelector />
<Results />
</>
)}
</View>
);
};
export default DaySelect;
DateSelector is just the Component where the date is selected; any change to the date updates the value of dateState.currentDate. Results displays the data.
This works fine as long as the user clicks a date, and waits for the results to show before clicking a new date. However, if they click several times, then a request is fired off each time and the resulting data is displayed as each request completes. Since the requests don't finish in the order that they start, this often results in the wrong data being shown.
To avoid this, I believe I need to cancel any existing requests before making a new request. I've tried to do this using Abort Controller, but it doesn't seem to do anything.
I added the following to myApi.js:
const controller = new AbortController();
class client {
axiosClient = axios.create({
baseURL: example.com,
});
async get(url, data, config) {
return this.axiosClient.get(url, data, config);
}
async put(url, data, config) {
return this.axiosClient.put(url, data, config);
}
async post(url, data, config) {
return this.axiosClient.post(url, data, config, {signal: controller.signal});
}
async cancel() {
controller.abort();
}
}
export default new client();
Then in my main component I do
myApi.cancel()
before making the new request.
This doesn't seem to do anything, though - the requests don't get cancelled. What am I doing wrong here?
EDIT: Following Ibrahim's suggestion below, I changed the api file to:
const cancelTokenSource = axios.CancelToken.source();
class client {
axiosClient = axios.create({
baseURL: example.com,
});
async post(url, data, config) {
const newConfig = {
...config,
cancelToken: cancelTokenSource.token
};
return this.axiosClient.post(url, data, newConfig);
}
async cancel() { // Tried this with and without async
cancelTokenSource.cancel();
}
}
export default new client();
This makes the api call fail entirely.
Step1: Generate cancel token
const cancelTokenSource = axios.CancelToken.source();
Step2: Assign cancel token to each request
axios.get('example.com/api/getDataForDate', {
cancelToken: cancelTokenSource.token
});
// Or if you are using POST request
axios.post('example.com/api/postApi', {data}, {
cancelToken: ancelTokenSource.token,
});
Step3: Cancel request using cancel token
cancelTokenSource.cancel();
In myApi I used AbortController to ensure that any cancellable requests are aborted when a new cancellable request comes in:
let controller = new AbortController();
class client {
axiosClient = axios.create({
baseURL: example.com,
});
async post(url, data, config, stoppable) {
let newConfig = {...config};
// If this call can be cancelled, cancel any existing ones
// and set up a new AbortController
if (stoppable) {
if (controller) {
controller.abort();
}
// Add AbortSignal to the request config
controller = new AbortController();
newConfig = {...newConfig, signal: controller.signal};
}
return this.axiosClient.post(url, data, newConfig);
}
}
export default new client();
Then in my component I pass in 'stoppable' as true; after the call I check whether the call was aborted or not. If not, I show the results; otherwise I ignore the response:
useEffect(() => {
const load = () => {
const url = '/getDataForDate';
const req = {
selectedDate: moment(dateState.currentDate).format(
'YYYY-MM-DD',
),
};
myApi
.post(url, req, null, true)
.then((res) => {
if (!res.config.signal.aborted) {
// Do something with the results
}
})
.catch((err) => {
// Show an error if the request has failed entirely
});
};
load();
}, [dateState.currentDate]);
I have TextInput and I need to send request every time when the text is changing
I have this code:
// Main.js
import Api from 'network/ApiManager';
const api = new Api();
// TextInput onChangeText function
const getSearch = useCallback(
async (searchName, sectorTypeId, type, filterData) => {
const result = await api.controller.search(searchName, sectorTypeId, type, filterData);
console.log(result)
},
[],
);
And i have this network layer
// NetworkManager.js
async getData(url) {
try {
const {data: response} = await axios.get(url);
return response;
} catch (e) {
return response;
}
}
controller = {
profile: async (search, sector, f_type, filterData = {}) => {
const res = await this.getData('/url/path');
return this.transformToOptions(res);
},
};
When onChangeText is called, I send a lot of requests, but I want to cancel previous requests and get the latest only. I know that I need to use CancelToken but I don't know how to pass it on my network layer
Please help
You can create a cancelToken, whenever a request comes, you can save the cancel token, when a new request comes, cancelToken won't be undefined, thus you can call cancelToken.cancel(). Try something like this:
let cancelToken
if (typeof cancelToken != typeof undefined) {
cancelToken.cancel("Operation canceled due to new request.")
}
//Save the cancel token for the current request
cancelToken = axios.CancelToken.source()
try {
const results = await axios.get(
`Your URL here`,
{ cancelToken: cancelToken.token } //Pass the cancel token
)
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>;
};
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);
}
}