AppState removeEventListener not removing listener - react-native

Simple code:
useEffect(() => {
AppState.addEventListener("change", handleChangeEvent);
return (() => {
console.log("REMOVING EVENT LISTENER");
AppState.removeEventListener("change", handleChangeEvent);
});
}, []);
const handleChangeEvent = () => {
console.log("EVENT LISTENER FIRING");
};
Navigate to new screen:
const changeScreen = () => {
return props.navigation.navigate("MainView", {})
}
When arriving at new screen: The REMOVING EVENT LISTENER fires. But in this new screen, the event listener is still listening.
Why is it not being removed?

try using useFocussedEffect instead,
useFocusEffect(
React.useCallback(() => {
AppState.addEventListener("change", handleChangeEvent);
return () => {
AppState.removeEventListener("change", handleChangeEvent);
}
}, [])
);

Related

How should I disconnect from signalR when leaving a screen in react-native

In my app I have a few screens that use signalR like that. That function is called useEffect function and it works:
const setupSignalR = () =>
{
SecureStore.getItemAsync("token").then(tk => {
let connection = new HubConnectionBuilder()
.withUrl("URL", {
accessTokenFactory: () => tk
})
.build();
connection.on("Update", function (message) {
//DOSTUFF
});
connection.start()
.then(() => console.log("connection started"))
.catch(err => console.log("connecting hub failed err is : ", err));
});
}
The problem is that if I leave the screen the connection stays open, and when I return to the screen I open another connection which means I now have 2 connections open at the same time.
I know that signalR has a stop function that I can call, so I tried to use the navigation listeners like that, but they aren't called:
useEffect(() =>
{
Load();
setupSignalR();
const unsubscribe = navigation.addListener('focus', () => {
});
const sub = navigation.addListener('blur', () => {
console.log("============");
});
}, [navigation]);
I generally leave a screen by pressing the back-button or by using navigation.navigate();
return () => {
connection.stop();
}
Works.
I think you need connection.
const setupSignalR = () => {
SecureStore.getItemAsync("token").then(tk => {
let connection = new HubConnectionBuilder()
.withUrl("URL", {
accessTokenFactory: () => tk
})
.build();
connection.on("Update", function (message) {
//DOSTUFF
});
connection.start()
.then(() => console.log("connection started"))
.catch(err => console.log("connecting hub failed err is : ", err));
return connection;
});
}
useEffect(() => {
Load();
let connection = setupSignalR();
const unsubscribe = navigation.addListener('focus', () => {
});
const sub = navigation.addListener('blur', () => {
console.log("============");
});
return () => {
connection.stop();
}
}, [navigation]);

useState is not updating the State

I want to set some token value when user sign In and for that I've created a state by useState hook. But when i call the setter function the value is not updating.
App.js
const [userToken, setUserToken] = useState(false)
const authContext = useMemo(() => ({
signIn: () => {
setUserToken(true)
console.log('sign in called')
console.log('userToken: ',userToken)
setIsLoading(false)
},
signOut: () => {
setUserToken(false)
console.log('sign out called')
console.log('userToken: ',userToken)
setIsLoading(false)
},
signUp: () => {
setUserToken(true)
console.log('sign up called')
setIsLoading(false)
},
}))
Login.js
const { signIn } = useContext(AuthContext)
const handleSubmit = () => {
if (isValidEmail && isValidPassword) {
signIn()
}
}
<TouchableOpacity style={styles.signInCont} onPress={handleSubmit}>
<Text style={styles.signInTxt}>Sign In</Text>
</TouchableOpacity>
everytime userToken is 'false'.
The way I like to think about the useState modifier function is that it is asynch; i.e., the update is not immediate.
This is why you would probably want to look at a useEffect hook to handle the side effect of the state change; for instance:
const [userToken, setUserToken] = useState(false)
const authContext = useMemo(() => ({
signIn: () => {
setUserToken(true)
console.log('sign in called')
setIsLoading(false)
},
signOut: () => {
setUserToken(false)
console.log('sign out called')
setIsLoading(false)
},
signUp: () => {
setUserToken(true)
console.log('sign up called')
setIsLoading(false)
},
}))
useEffect(() => console.log('userToken: ', userToken), [userToken])
The above (if I've typed it right) will now print the state update, as a Side Effect of the state change; so for instance, if you are setting the userToken, then the time to fetch some data (say the user profile) would be in the side effect of the state change.
Please see: https://reactjs.org/docs/hooks-effect.html

react native unsubscribe event listener

I have a react native component with two event listeners for linking and for dynamicLinks, how do I unsubscribe for both using hooks?
useEffect(() => {
// Update the document title using the browser API
if (Platform.OS === "ios") {
SecurityScreen.enabled(true);
}
// global.perra = "a";
usingAlternativeAPI();
Linking.addEventListener("url", deepLinkHandler);
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
// When the component is unmounted, remove the listener
return () => unsubscribe();
}, []);
Linking lib has a removeEventListener() function you can call with passing the url event type and the handler. This code should work.
useEffect(() => {
// useEffect code here
return function cleanup() {
unsubscribe();
Linking.removeEventListener("url", deepLinkHandler);
};
}, []);
Have you tried this before?
useEffect(() => {
// Update the document title using the browser API
if (Platform.OS === "ios") {
SecurityScreen.enabled(true);
}
// global.perra = "a";
usingAlternativeAPI();
const un = Linking.addEventListener("url", deepLinkHandler);
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
// When the component is unmounted, remove the listener
return () => {
unsubscribe();
un()
}
}, []);
At the moment the documentation points to do this way,
useEffect(() => {
const unsub = Linking.addEventListener("url", ({ url: _url }) => {
setUrl(_url);
});
return unsub.remove();
}, []);

How to use focus and blur listener in single useEffect react native

As you know in useEffect we return the unsubscribe at the end if we assign any listener to unsubscribe const as shown under
As we Using
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// code
})
return unsubscribe;
}, [navigation]);
As I want
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// code
})
const unsubscribe2 = navigation.addListener('blur', () => {
// code
})
// need to return both listeners
}, [navigation]);
You can cleanup like this
useEffect(() => {
navigation.addListener('focus', handler)
navigation.addListener('blur', handler)
return () => {
navigation.removeListener('focus', handler)
navigation.removeListener('blur', handler)
}
},[navigation])
The official example here https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup
I didn't test this, but you might be able to do something like this:
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// code
});
const unsubscribe2 = navigation.addListener('blur', () => {
// code
});
return () => {
// executed when unmount
unsubscribe();
unsubscribe2();
}
}, [navigation]);

Calling componentWillMount every time focused page in react native

I want call the componentWillMount every time that I focused a page. I using react-redux and react-navigation.
With react-navigation I use import { withNavigationFocus } from 'react-navigation'; to detect if the page was active but when I call componentDidMount there ara a few seconds that I see old view. Is for this I want calling componentWillMount instead of componentDidMount when page focused.
This is my code:
class HomeScreen extends React.Component {
componentWillMount() {
this.props._loading(true);
}
omponentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener('didFocus', () => {
this.setState({loading: 0});
this.props._loading(true);
Api.get('s?type=Featured')
.then( response => {
if (response.profiles){
this.setState({featured_users: response.profiles, loading: this.state.loading + 1});
}
}).catch((error) => {
console.log(error);
this.props._loading(false);
});
Api.get('s?type=Top')
.then( response => {
if (response.profiles){
this.setState({featured_top: response.profiles, loading: this.state.loading + 1});
}
}).catch((error) => {
console.log(error);
this.props._loading(false);
});
});
}
componentWillUnmount() {
// Remove the event listener
this.focusListener.remove();
}
render() {
if (this.state.loading >= 4){
this.props._loading(false);
}
return (
...
);
}
}
const mapStateToProps = (state) => ({
user: state.reducerUser,
loading: state.reducerGeneral
});
mapDispatchToProps = dispatch => {
return {
_loading: loading => {
dispatch(actionLoading(loading));
},
updateUser: user => {
dispatch(actionUser(user));
},
}
}
export default withNavigationFocus(connect(mapStateToProps, mapDispatchToProps)(HomeScreen));
You can add this inside your componentWillMount and whatever you write inside your addListener , it will be executed everytime:
this.focusListener = this.props.navigation.addListener('didFocus', () => {
// The screen is focused
this.getData();
});