removeEventListener is Deprecated and i don't achieve to refacto it properly - react-native

Linking.removeEventListener('url', onReceiveURL);
removeEventListener is deprecated.
This is what my IDE suggests :
EventEmitter.removeListener('url', ...): Method has been deprecated.
Please instead use remove() on the subscription returned by
EventEmitter.addListener.
// Custom function to subscribe to incoming links
subscribe(listener: (deeplink: string) => void) {
// First, you may want to do the default deep link handling
const onReceiveURL = ({url}: {url: string}) => listener(url);
// Listen to incoming links from deep linking
Linking.addEventListener('url', onReceiveURL);
const handleDynamicLink = (
dynamicLink: FirebaseDynamicLinksTypes.DynamicLink,
) => {
listener(dynamicLink.url);
};
const unsubscribeToDynamicLinks = dynamicLinks().onLink(handleDynamicLink);
return () => {
unsubscribeToDynamicLinks();
Linking.removeEventListener('url', onReceiveURL);
};
I tried many things but nothing seems to work.
Didn't find any concrete information about it.
Any help to figure it out ?
EDIT -> I will investigate further but so far it's working :
const unsubscribeToDynamicLinks : any = ...
then in return :
return () => {
unsubscribeToDynamicLinks().remove('url', onReceiveURL);};

For the new API, this is how it works:
useEffect (() => {
const subscription = Linking.addEventListener('url', onReceiveURL);
return () => subscription.remove();
}, [])

For class components you can use something like below:
class MyClass extends Component {
constructor(props){
super(props)
this.changeEventListener = null
}
componentDidMount(){
// or you can use constructor for this
this.changeEventListener = AppState.addEventListener('change', () => {
// your listener function
})
}
componentWillUnmount() {
this.changeEventListener.remove()
}
}

Related

Jest Testing onTouchStart/onTouchEnd

I've got a component that uses the onTouchStart and onTouchEnd and I can't figure out how to test it.
Here's a snack for the code, but the snippet is below:
export default function TouchComponent({
children,
onStart = doNothing,
onEnd = doNothing
}: TouchComponentProps): React.ReactElement {
return (
<View
onTouchStart={(e) => onStart()}
onTouchEnd={(e) => onEnd()}
>{children}</View>
);
}
const doNothing = () => {};
interface TouchComponentProps {
children?: React.ReactNode;
onStart?: () => void;
onEnd?: () => void;
}
Looking at the documentation, there aren't any methods listed for onTouchStart/onTouchEnd, but this method array seems to suggest that it has other methods that can be invoked, but it doesn't look like it works here because fireEvent["onTouchStart"](myComponent); fails with an error saying: TypeError: _reactNative.fireEvent.onTouchStart is not a function.
I've searched around but can't seem to find any documentation or other questions about testing onTouchStart and onTouchEnd, so how do I fire these events?
I figured this out almost immediately after asking this question. In my situation, I can invoke them like this:
import TouchComponent from "../TouchComponent ";
import { render, fireEvent } from "#testing-library/react-native";
it("Invokes onTouchStart and onTouchEnd", () => {
const { getByTestId } = render(
<TouchComponent
onStart={() => {
console.log("onTouchStart invoked");
}}
onEnd={() => {
console.log("onTouchEnd invoked");
}}
testID="my-component" />);
const myComponent = getByTestId("my-component");
// Passing empty {} data, but may need to supply for other use cases.
fireEvent(myComponent, "onTouchStart", {});
fireEvent(myComponent, "onTouchEnd", {};
});
// Console:
// console.log
// onTouchStart invoked
// console.log
// onTouchEnd invoked

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;
})
}

can't remove android backHandler

I find this code to handle the back button on android :
componentDidMount() {
this.handleAndroidBackButton ();
}
componentWillUnmount () {
this.removeAndroidBackButtonHandler();
}
handleAndroidBackButton = () => {
BackHandler.addEventListener('hardwareBackPress', () => {
this.showModal1(true);
return true;
});
};
removeAndroidBackButtonHandler = () => {
BackHandler.removeEventListener('hardwareBackPress', () => {});
}
it works fine but when I go to the other page, I still have the same behaviour.
I find this on stackoverflow:
constructor() {
this._onBackButton= this._onBackButton.bind(this)
}
_onBackButton() {
return true;
}
and I changed my code to this:
constructor(props){
super(props)
this.state = {
transData: [],
...
}
this._onBackButton = this._onBackButton.bind(this);
}
_onBackButton = () => {
return true;
};
componentDidMount() {
this.handleAndroidBackButton();
...
}
componentWillUnmount () {
this.removeAndroidBackButtonHandler();
}
handleAndroidBackButton = () => {
BackHandler.addEventListener('hardwareBackPress', this._onBackButton);
};
removeAndroidBackButtonHandler = () => {
BackHandler.removeEventListener('hardwareBackPress', this._onBackButton);
}
I don't have any error now but it doesn't work ! it doesn't remove event listener in other screens
The AndroidBakHandler you're having is named _onBackButton ... but you're binding a method called _onBack ... So just rename your backHandler to be _onBackButton and make it arrow-function (Auto bound to your class ... that's why you wouldn't need to bind it in the constructor)
// constructor() {
// No need for the next line ... arrow function is bound automatically
// this._onBackButton = this._onBackButton.bind(this);
// }
_onBackButton = () => {
return true;
};
-
removeAndroidBackButtonHandler = () => {
BackHandler.removeEventListener('hardwareBackPress',
() => {}); // <- look at that
}
Regarding: "It works fine but when I go to the other page, I still have the same behaviour."
It's because when you navigate into another screen (Your component is not unmounted) ... and you're removing your android-back-handler in componentWillUnmount ...
So I'd suggest you remove your event-listener when you navigate to another screen not in componentWillUnmount
Also make sure you have you add your android-back-handler when your screen receives focus again

How do you remove a listener from React Native's EventEmitter instance?

There is removeCurrentListener, but no removeListener method.
I found the answer myself.
https://github.com/facebook/react-native/blob/235b16d93287061a09c4624e612b5dc4f960ce47/Libraries/vendor/emitter/EventEmitter.js
addListener returns a EmitterSubscription instance that extends EventSubscription that has remove method.
https://github.com/facebook/react-native/blob/235b16d93287061a09c4624e612b5dc4f960ce47/Libraries/vendor/emitter/EventSubscription.js
const emitter = new EventEmitter();
const subscription = emitter.addListener('eventname', () => {});
subscription.remove(); // Removes the subscription
Actually it does (unless I'm misunderstanding your question).
Here's how I do it:
class Store extends EventEmitter {
constructor(listenerKey) {
super()
this.listenerKey = listenerKey
}
emitChange() {
setTimeout(() => {
this.emit(this.listenerKey)
}, 0)
}
addChangeListener(callback) {
this.on(this.listenerKey, callback)
}
removeChangeListener(callback) {
this.removeListener(this.listenerKey, callback)
}
}

Promise isn't working in react component when testing component using jest

Good day. I have the following problem:
I have an item editor.
How it works: I push 'Add' button, fill some information, click 'Save' button.
_onSaveClicked function in my react component handles click event and call function from service, which sends params from edit form to server and return promise.
_onSaveClicked implements
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
})
function and waits for promise result. It works in real situation.
I created fake service and placed it instead of real service.
Service's function contains:
return Promise.resolve({data: 'test response'});
As you can see fake service return resolved promise and .then() block should work immediatly. But .then() block never works.
Jest test:
jest.autoMockOff();
const React = require('react');
const ReactDOM = require('react-dom');
const TestUtils = require('react-addons-test-utils');
const expect = require('expect');
const TestService = require('./service/TestService ').default;
let testService = new TestService ();
describe('TestComponent', () => {
it('correct test component', () => {
//... some initial code here
let saveButton = TestUtils.findRenderedDOMComponentWithClass(editForm, 'btn-primary');
TestUtils.Simulate.click(saveButton);
// here I should see response in my console, but I don't
});
});
React component save function:
_onSaveClicked = (data) => {
this.context.testService.saveData(data)
.then(response => {
console.log('I\'m in then() block.');
console.log('response', response.data);
});
};
Service:
export default class TestService {
saveData = (data) => {
console.log('I\'m in services saveData function');
return Promise.resolve({data: data});
};
}
I see only "I'm in services saveData function" in my console.
How to make it works? I need to immitate server response.
Thank you for your time.
You can wrap your testing component in another one like:
class ContextInitContainer extends React.Component {
static childContextTypes = {
testService: React.PropTypes.object
};
getChildContext = () => {
return {
testService: {
saveData: (data) => {
return {
then: function(callback) {
return callback({
// here should be your response body object
})
}
}
}
}
};
};
render() {
return this.props.children;
}
}
then:
<ContextInitContainer>
<YourTestingComponent />
</ContextInitContainer>
So your promise will be executed immediately.