React Native infinite loop - react-native

I declared and initialized the variable like this:
const [user, setUser] = useState(null)
Then make a function like this:
const getUser2 = async () => {
try {
const user2 = await AsyncStorage.getItem('user')
let parsed = JSON.parse(user2)
setUser(parsed)
console.warn('1')
} catch(err) {}
}
Then call it like this:
useEffect(() => {
getUser2()
return () => getUser2()
})
The problem is when I run it, it produces an infinite loop like this:
Why does it loop infinitely?

Add dependency array, Then it will get called only once -
useEffect(() => {
getUser2()
return () => getUser2()
}, [])
Go through this reference for better understanding of useEffect - https://blog.logrocket.com/guide-to-react-useeffect-hook/
In your useEffect you haven't added any dependency array, so on each re-render this useEffect is getting called, when you add [ ] dependency as a parameter to useEffect then it will act as componentDidMount which get called only once.

useEffect will be triggered every time the setState is set, if you do not pass the second parameter

Related

React native UseEffect and async issue

I'm trying to get a StoreKey from firestore (v9), and put it inside another collection of DB as a path.
for example, get storeKey (132, for example) and put inside
collection(db, 'store', storeKey, 'coffeeDB') to access specific sub collection. I put two function (1: getData (storeKey), 2: access to sub collection) into UseEffect so that it can run when it's mounted.
However, I found UseEffect runs twice, initial storeKey shows Array [], and the next run gets proper value which is 132. So, I'm having an error due to the first run.
I guess it's because the second function inside UseEffect does not wait for getData function to watch the data, but not too sure.
How can I resolve this issue??
const getData = async(setStoreKey, setName) => {
console.log('xxxx')
const auth = getAuth();
const user = auth.currentUser;
if(user !== null){
const email = user.email;
const UserInfo = await getDoc(doc(db, 'users', email));
if(UserInfo.exists()){
setStoreKey(UserInfo.data().storeKey)
setName(UserInfo.data().name);
}
else{
console.log('None')
}
return
}
}
T
const StockScreen = ({ navigation }) => {
const [storeKey, setStoreKey] = useState([]);
const [userName, setName] = useState([]);
const [coffeeStock, setCoffeeStock] = useState([]);
useEffect(() => {
getData(setStoreKey, setName);
const unsub = onSnapshot(collection(db, 'Store', storeKey, 'coffeeDB'), (snapshot) => {
setCoffeeStock(snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
number: doc.data(),
})));
});
return unsub;
}, [storeKey]);
Just remove dependency from second argument in useEffect and pass blank array
useEffect(() => {
// your code
}, []);
It will run only once when your component is loaded. It is similar to componentDidMount of class component.
The reason your useEffect run twice is because your storeKey state changing in getData(setStoreKey, setName) function. So what you can do here if you want to call getData() function once is to declare it on a separate useEffect function like:
useEffect(() => {
getData(setStoreKey, setName); //call your getData function once
}, []);
And what I see is you need to update StoreKey every time for the unsub listener so with that above useEffect call another useEffect whenever the StoreKey dependency change like:
useEffect(() => {
getData(setStoreKey, setName); //call your getData function once
}, []);
useEffect(() => { //another useEffect whenever storeKey changes
const unsub = onSnapshot(collection(db, 'Store', storeKey, 'coffeeDB'), (snapshot) => {
setCoffeeStock(snapshot.docs.map((doc) => ({
id: doc.id,
data: doc.data(),
number: doc.data(),
})));
});
return unsub;
}, [storeKey]);
Hope this works for you.

useState in React Native get data of previous state

I have one state
const [data, setData] = useState("");
And 2 useEffects that call in parallel when component renders
useEffect(() => {
socket.on("message",()=>{
console.log(data)
})
}, [socket])
useEffect(() => {
const res = getDataFromServer()
setData(res.data)
}, [isLoading])
2nd useEffect get data from server and set state but when socket arrive in first useEffect data is on initial state that is empty. How can I get updated state data in first useEffect when socket arrives. If I set data as dependency to first useEffect then socket event is reinitialized and callback is calling multiple times.
You can return a function in useEffect to clean unnecessary handlers / event listeners.
Effects with Cleanup - React Docs
In this function you can use the offAny method of socket.io client to remove previous listener.
useEffect(() => {
const currentListener = socket.on("message",()=>{
console.log(data)
});
return () => {
socket.offAny(currentListener);
};
}, [socket, data]);
This might help
React.useEffect(() => {
// This effect only executes on the initial render so that we aren't setting up the socket repeatedly.
socket.on("message",()=>{
console.log(data);
})
return () => {
socket.off('message',() => {});
}
}, []);

Async custom hook from within useEffect

When kept in the component body, the following code works fine. Inside useEffect, it checks the asyncstorage and dispatches an action (the function is longer but other checks/dispatches in the function are of the same kind - check asyncstorage and if value exists, dispatch an action)
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
I'm trying to move it to a custom hook but am having problems. The custom hook is:
const useGetUserSettings = () => {
const dispatch = useDispatch();
useEffect(() => {
const getSettings = async () => {
const aSet = await AsyncStorage.getItem('aSet');
if (aSet) {
dispatch(setASet(true));
}
};
getSettings();
}, [dispatch]);
};
export default useGetUserSettings;
Then in the component where I want to call the above, I do:
import useGetUserSettings from './hooks/useGetUserSettings';
...
const getUserSettings = useGetUserSettings();
...
useEffect(() => {
getUserSettings();
}, [getUserSettings])
It returns an error:
getUserSettings is not a function. (In 'getUserSettings()', 'getUserSettings' is undefined
I've been reading rules of hooks and browsing examples on the internet but I can get it working. I've got ESlint set up so it'd show if there were an invalid path to the hook.
Try the following.
useEffect(() => {
if (!getUserSettings) return;
getUserSettings();
}, [getUserSettings]);
The hook doesn't return anything, so it's not surprising that the return value is undefined ;)

How to use addListener in useEffect

I want to run method when focus screen, i use this:
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
console.log(
'test'
);
});
return unsubscribe;
}, [navigation]);
but it doesnt work. it gives an error like this :
*
An effect function must not return anything besides a function, which
is used for clean-up. You returned: [object Object]
also even i dont return anything, console.log(
'test'
) doest work
I am using navigation V4
Is this working?
import { useFocusEffect } from '#react-navigation/native';
......
useFocusEffect(useCallback(() => {
......
console.log(something)
}, [something]));
//-------
If not check if react navigation is configured correctly.
UPDATE
In React navigation 4.x you will have to follow one of the methods in this guide https://reactnavigation.org/docs/4.x/function-after-focusing-screen/
For useEffect to work properly, the flow is following:
in the square brackets in the end you add a variable which triggers the action. In your case it only triggers on the firs run, and on navigation variable change
you should run your function within useEffect. You have only defined a constant in the body of useEffect, but you never run it.
optionally you may return a function in the end of a run. This function is triggered only when the component unmounts, and used to avoid memory leaks.
Based on this: I'm not sure what are you trying to achieve (unclear from your original post), but this may be what you want:
useEffect(() => {
navigation.addListener('focus', () => {
console.log(
'test'
);
});
const unsubscribe = () => navigation.removeListener('focus'); // !!! I'm not sure about this one, check the docs how to unsubscribe !!!
return unsubscribe;
}, [navigation]); // << triggers useEffect
Assuming you are using the latest version of react-navigation you must the use-focus-effect.
https://reactnavigation.org/docs/use-focus-effect/
Your code should be updated as mentioned below
useFocusEffect(
useCallback(() => {
const unsubscribe = () => {
console.log("test");
}
return () => unsubscribe();
}, [userId])
);

How do I use an event-listener inside useEffect?

I am trying to move some code from a React Native class component into a Functional Component. The original code looks like this:
componentDidMount(){
this.state.broadcastSubscriber = DeviceEventEmitter.addListener('event', (intent) =>{ this.broadcastReciever(intent)});
}
What I have tried so far is different permutations of:
useEffect(() => {
const broadcastSubscriber = DeviceEventEmitter.addListener('event', (intent) =>{ broadcastReciever(intent)});
}}, []);
Including things like making broadcastReciever a useCallback function. So far though broadcastReciever never hears anything outside of when it is first ran in useEffect.
Could anyone point out what I am doing wrong? Thanks.
PS: I am aware DeviceEventEmitter is depreciated.
try the following:
const broadcastReciever = (intent)=>{ //do you intent code here}
useEffect(() => {
DeviceEventEmitter.addListener('event', broadcastReciever);
return DeviceEventEmitter.removeListener('event', broadcastReciever);
}}, []);
You can add Event Listener in useEffect like this
useEffect(() => {
const loads = props.navigation.addListener('didFocus', () => {
//do something here on Listner
});
return () => {
loads
}}, [props.navigation]);