How to restrict call multiple api call in useEffect in react-native? - react-native

I am new in React-Native, I am facing one issue in my app and not able to resolved that yet. All thing is working but first time API being calling two time unnecessary.
Here is my useEffect method :-
useEffect(() => {
if (transactions.length === 0) {
setIsAPICall(true);
}
getTransactions().then((response: any) => {
dispatch(actionTransitionsBadge(0));
setTransactions(response);
setIsAPICall(false);
});
}, [user, onChainBalance]);
In this method I have to get transaction list when component first time open. After that I have to refresh this list when user and onChainBalance get updated. The thing is working but when I am loading this component first time then the api is calling multiple time.
What I can do to manage this flow that once component load then api call once after then when my two state changed the api call again.

Put your getTransactions in the useCallback, like this:
const fetchData=useCallback(
() => {
getTransactions().then((response: any) => {
dispatch(actionTransitionsBadge(0));
setTransactions(response);
setIsAPICall(false);
});
},
[],
)
Then call fetchData in the useEffect

Here is some Details where you can useEffect .
useEffect(() => {},)
useEffect(() => {},[])
useEffect(() => {},[dependency_array])
Here I am explaining them one by one
The first would call on every render
The Second would call two time
the third would call two times and when the dependency array changes
Here is your use Case
useEffect(() => {
if(user !== null && user !== undefined && transactions.length === 0){
getTransactions().then((response: any) => {
dispatch(actionTransitionsBadge(0));
setTransactions(response);
setIsAPICall(false);
});
}
}, [user, onChainBalance]);
but Still this is not a good method. you should use react-query or redux-toolkit-query

Related

Why use Hub.listen inside useEffect - Amplify Auth

I'm looking through the documentation at https://docs.amplify.aws/lib/auth/social/q/platform/js/#full-samples
and can't understand why Hub.listen is being used within use Effect.
Hub.listen('auth', ({ payload: { event, data } }) => {
switch (event) {
case 'signIn':
case 'cognitoHostedUI':
getUser().then(userData => setUser(userData));
break;
}
});
If I'm creating an event listener why should I create in useEffect instead of in the main body of the function.
What am I misunderstanding?
Figured it out, I was getting confused between functions and classes.
The useEffect with an empty array is being used to only create the event listener on initial render and not on subsequent renders.
Leaving the question up in case anyone else gets similarly confused
This has little to do with functions and classes. It is inside the useEffect(() => {/*...*/}, []) (note the empty dependency list) as it acts as the constructor of the component and is therefore only called once.
If Hub.listen is called inside the render function, this will result in growing list of event listeners, because everytime the component rerenders, a new listener is attached.
To keep you free from any memory issues, you would also detach a listener inside the useEffect hook. So for common listeners, would look like the following:
const App = () => {
useEffect(() => {
const listener = () => { console.log('foo'); }
window.addEventListener('resize', listener);
// destruct
return () => {
window.removeEventListener('resize', listener)
}
}, []);
}

React Native Doesn`t re-render after context state change

state changes after getting data from api. i can see that with console.log()
but doesn`t render data after update. there is my code fetch code :
useEffect(() => {
axios
.get(
'https://mylink.ngrok.io/channels/getUser',
).then((data)=>{
let array = [];
const promises = data.data.channels.map((channel)=>{
axios.get(`https://mylink.ngrok.io/channels/getChannel/${channel}`).then((resp)=>{
array.push(resp.data);
})
})
Promise.all(promises).then(()=>{
setonLineChannels(array);
});
})
}, []);
btw its in context.
I think Promise.all casue of that
thx
Where you declare promises, it's not going to wait for those API requests to complete. They're running even before you've called Promise.all(promises).
You can just put the code you've defined as promises inside the Promise.all() directly, then make sure you return the get request inside. And don't push to array. Your map will return an array of promises, and you can use the results array in the .then after Promise.all to update your state directly.
Here's what I mean:
useEffect(() => {
axios.get("https://mylink.ngrok.io/channels/getUser").then((data) => {
Promise.all(
data.data.channels.map(
(channel) =>
axios
.get(`https://mylink.ngrok.io/channels/getChannel/${channel}`)
.catch((err) => console.error(err)) // this allows an individual request to fail
)
).then((results) => setonLineChannels(results));
});
}, []);

Eslint hook refactor useEffect

I create useEffect like that. I want useEffect listen useToken and when I login and set token it will fetch some data
useEffect(() => {
if (userToken) {
setTimeout(() => {
fetchDataWishlistShow();
fetchCart();
}, 500);
}
fetchCategory();
}, [userToken]);
But Eslint of hook automatic add some function to Effect
useEffect(() => {
if (userToken) {
setTimeout(() => {
fetchDataWishlistShow();
fetchCart();
}, 500);
}
fetchCategory();
}, [fetchCart, fetchCategory, fetchDataWishlistShow, userToken]);
Why it do that. I think it wrong but anyone can explain for me?
I guess you installed "eslint-plugin-react-hooks" and enable it.
useEffectis designed the way that which ever you use in you useEffect you are recommended to add it it dependencies array list EXCEPT some things that React guarantee that they are not changed every each re-render such as: dispatch of useReducer, setState form const [state, setState] = useState() or functions or variables imported from other files.
You can turn off the rule but you SHOULD NOT do that because you did not solve the source of the problem. E.g
const [cartStatus, setCartState] = useState("all") // assume it can be "all", "pending"
const fetchCart = () =>{
fetch(`endpoint/cart/${cardStatus}`)
}
useEffect(() => {
if (userToken) {
setTimeout(() => {
fetchDataWishlistShow();
fetchCart();
}, 500);
}
fetchCategory();
// This case you alway fetch all item in cart
}, [userToken]);
To fix issue above you saying you saying, yeah we can just add fetchCart into dependency list, but it'll cause infinite re-render, you need to wrap fetchCart by useCallback or move fetchCart into useEffect. Because you call the function inside setTimeout, you might want to clean the useEffect
const [cartStatus, setCartState] = useState("all") // assume it can be "all", "pending"
useEffect(() => {
const fetchCart = () =>{
fetch(`endpoint/cart/${cardStatus}`)
}
let id = null
if (userToken) {
id = setTimeout(() => {
fetchDataWishlistShow();
fetchCart();
}, 500);
}
fetchCategory();
return () => clearTimeout(id);
}, [userToken]);
This article is written by Dan Abramov is a good resource to look at and deep dive into how useEffect works and why you should follow the recommended way.
You might saying that "No no, I am only have api call when the component mounted, That's it". But when your project grown, and you components become complicated, it's hard to remember that, who's know that your requirements might be changed at some point in the future, why don't do it in proper way to give you more confident when refactor or update your components?

Getting updated redux state after dispatch with react hooks

I'm using the action expensesActions.getExpenseList to get a list from the database, which in turns updates the store in expense.expenseList
I'm calling the action inside useEffect hook and would like to get back the list from the store once it's retreived.
My code below is not working because the order is incorrect, if I refresh (with save) I do have the list. How can I change the code so my list is retreived once the actions is complete?
const fetchedList = useSelector(state => state.expense.expenseList);
// Get expense list
useEffect(() => {
const loadList = async () => {
setIsLoading(true)
await dispatch(expensesActions.getExpenseList())
calculateAverageExpense()
}
loadList()
}, [dispatch]);
You can have a second useEffect like this:
useEffect(() => {
if (fetchedList.length) calculateAverageExpense();
}, [fetchedList])
This will execute calculateAverageExpense every time your list change.

how to map nested array in componentWillMount using react native

I am creating react-native app. I have an array and want to use value outside the looping. I have declared value in this.state={current_cat_id:'',} I have tried it in componentWillMount like:
componentWillMount() {
var ids = [];
this.props.data.map((dataImage,Index) => {
dataImage['main-head'].map((subchild,Index2) => {
ids.push(subchild['path'])
})
})
this.setState({current_cat_id: ids})
}
its returning blank page. is this right approch
it should work for you. try this:-
componentWillMount() {
var ids = [];
this.props.data.map((dataImage) => {
dataImage['main-head'] != undefined && dataImage['main-head'].map((subchild) => {
ids.push(subchild['path'])
})
})
this.setState({current_cat_id: ids})
}
componentWillMount is called before the render method is executed. It is important to note that setting the state in this phase will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method. Use componentDidMount() instead.