useEffect unable to run in react native - react-native

I am using redux in react native , When the state of redux changes, useEffect is not executed,
here is my code
const message = useSelector(state => {
const { privateChat } = state
const chatObj = privateChat.filter(s => s.user === replyer) // chatObj is an object
console.log('changed')
return chatObj[0]
})
useEffect(() => {
console.log('message1', message)
}, [message])
every time the 'changed' was printed, useEffect did not print out the data,I dont konw what went wrong

This might help
const privateChat = useSelector(state => state.privateChat);
const chatObj = privateChat.filter(s => s.user === replyer) // chatObj is an object
console.log('changed');
useEffect(() => {
console.log('message1', chatObj)
}, [privateChat]);

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.

problem with storing data from cache into useState in react native

const [loggedInData, setLoggedInData] = useState([]);
const getLoginFromCache = async() => {
try{
let logData = await AsyncStorage.getItem('#MyDish:loginData');
let parsed = await JSON.parse(logData);
console.log(parsed);
setLoggedInData(parsed);
}
catch(error){
console.log(error);
}
}
getLoginFromCache()
console.log(loggedInData);
the result console.log(loggedInData) is always empty. whereas when i console.log(parsed) doesn't return empty
Use redux persist for store data on cache and to state.
https://www.npmjs.com/package/redux-persist
React set state is async, so console.log(loggedInData); will always print the initializtion state of loggedInData, in this case this state is an empty array
Try to wrap your getLoginFromCache function inside an useEffect like this:
const [loggedInData, setLoggedInData] = useState([]);
useEffect(() => {
const getLoginFromCache = async() => {
try{
let logData = await AsyncStorage.getItem('#MyDish:loginData');
let parsed = await JSON.parse(logData);
console.log(parsed);
setLoggedInData(parsed);
}
catch(error){
console.log(error);
}
}
getLoginFromCache()
}, [])
and if you want tracking your loggedInData state, you can use another useEffect to log that one:
useEffect(() => console.log(loggedInData),[loggedInData])

Updating a state variable in React Native Expo

I dont really understand how setState and state variables update and work in React Native. Im trying to figure out what I did wrong in the code below, because I'm updating my tokenArray variable, but when I console log it in another function it is empty. Please help.
constructor() {
super()
this.state = {
tokenArr: []
}
}
componentDidMount() {
this.grabToken()
}
firebaseInformation = async () => {
var tokens = []
firebase.database().ref(`tokens/`).once('value', snapshot => {
const token = Object.values(snapshot.val());
token.map((item) => {
tokens.push(item.data)
})
return this.setState({
tokenArr: tokens
})
})
}
grabToken = async () => {
this.firebaseInformation()
console.log(this.state.tokenArr)
}
The fix was just to call the grabToken function in my render method instead (I was only calling it from my componentDidMount and didn't understand why it wasn't updating my state variable properly.
Return the array and set the state in componentDidMount() like this
componentDidMount() {
this.firebaseInformation()
.then((arr) => this.setState({ tokenArr: arr }))
.then(this.state.tokenArr);
}
firebaseInformation = async () => {
var tokens = []
firebase.database().ref(`tokens/`).once('value', snapshot => {
const token = Object.values(snapshot.val());
token.map((item) => {
tokens.push(item.data)
})
return tokenArr;
})
}

how to handle the return values from reducer, inside a functional component

This may be a basic question, but I'm new to React Native and stuck here.
My code pasted below. reducer and functional component. I want to capture the response returned from reducer.
reducer.js
export const ActivationCenterReducer = (
state = INIT_KIT_STATE,
{ type, payload = {} }
) => {
switch (type) {
case 'KIT_ACTIVATION_SUCCESS_DATA': {
const { message, response_code, apiLoading, apiError } = payload;
return {
...state,
apiLoading: apiLoading,
apiError: apiError,
message: message,
response_code: response_code
};
}
// ...
}
// ...
};
Functional Component class:
const kitActivationCenter = ({ route, navigation }) => {
const response_code = useSelector(
store => store.kitActivationCenter.response_code
);
const handleKitActivation = () => {
/*This will call the validation() inside action.js and that follows the reducer.js file. where reducer.js file returning the values on success response. but I am not able to access that response_code returned from reducer.
How to save the response_code from the below dispatch function.*/
dispatch(Validation(locator, pin));
if (response_code === 200) {
// should navigate to the next screen
}
};
};
My question is how to capture the returned response_code from reducer.
I'm able to navigate to the next screen on clicking the submit button couple of times.I notice that first time when the dispatch function is called, the state of the response_code is not updating , hence the response_code != 200.
I want a way to capture the response and assign to variable.
Thanks in advance.
You are probably looking at the old value of response_code in your handleKitActivation.
const kitActivationCenter = ({ route, navigation }) => {
const response_code = useSelector(
store => store.kitActivationCenter.response_code
);
const handleKitActivation = () => {
dispatch(Validation(locator, pin));
// HERE the response_code does not have result value
// of your calling dispatch(Validation(locator, pin)) above yet
if (response_code === 200) {
// should navigate to the next screen
}
};
};
I suggest to move your response_code hanfling to the useEffect:
const kitActivationCenter = ({ route, navigation }) => {
const response_code = useSelector(
store => store.kitActivationCenter.response_code
);
// this effect will run whenever your response_code changes
useEffect(() => {
if (response_code === 200) {
// should navigate to the next screen
}
}, [response_code]);
const handleKitActivation = () => {
dispatch(Validation(locator, pin));
};
};

React Native hooks - correct use of useEffect()?

I'm new to hooks and ran across this setup on SO and wanted to confirm that this is the correct pattern. I was getting the RN "unmounted component" leak warning message before and this seemed to solve it. I'm trying to mimic in some way compnentDidMount. This is part of a phone number verify sign up flow and onMount I want to just check for navigation and then fire off a side effect, set mounted true and then unmount correctly.
const SMSVerifyEnterPinScreen = ({ route, navigation }) => {
const [didMount, setDidMount] = useState(false)
const { phoneNumber } = route.params
useEffect(() => {
if(navigation) {
signInWithPhoneNumber(phoneNumber)
setDidMount(true)
}
return () => setDidMount(false)
}, [])
if (!didMount) { return null }
async function signInWithPhoneNumber(phoneNumber) {
const confirmation = await auth().signInWithPhoneNumber('+1'+phoneNumber)
...
}
return (
...
)
}
RN 0.62.2 with react-nav 5 - thanks!
Since signInWithPhoneNumber is a async function and will setState you will see warning it the component is unmounted before the response is available
In order to handle such scenarios you can keep a variable to keep track whether its mounted or not and then only set state is the mounted variable is true
However you do not need to return null if component has unmounted since that doesn't accomplish anything. The component is removed from view and will anyways not render anything.
Also you do not need to maintain this value in state, instead use a ref
const SMSVerifyEnterPinScreen = ({ route, navigation }) => {
const isMounted = useRef(true)
const { phoneNumber } = route.params
useEffect(() => {
if(navigation) {
signInWithPhoneNumber(phoneNumber)
}
return () => {isMounted.current = false;}
}, [])
async function signInWithPhoneNumber(phoneNumber) {
const confirmation = await auth().signInWithPhoneNumber('+1'+phoneNumber)
...
}
return (
...
)
}