How to return a value from a React Native functional component to the parent - react-native

In the case of this functional component, how can I return a value to another component that calls the function?
/**
* A custom fetch wrapper.
*/
import Config from '../config'
const _Fetch = (context: Object, endpoint: string, init: Object, key?: string) => {
var k = (typeof key !== 'undefined') ? key : 'data';
return fetch(Config.base+endpoint, init)
.then(response => {
if (response.ok) {
response.text().then((text) => {
var obj = tryParseJSON(text)
if (obj) {
// For jsonapi.
if (obj.data) {
obj = obj.data
}
var s = {
data: Object
}
s[k] = obj
context.setState(s)
return s // This value exists here, but how can I return it to the _Fetch caller?
}
})
.catch((error) => {
console.error(error)
})
}
})
function tryParseJSON(jsonString) {
try {
var o = JSON.parse(jsonString)
if (o && typeof o === "object") {
return o
}
}
catch (e) {
console.log(e)
}
return false
}
}
export default _Fetch
When I try to get the value in another function, it is undefined:
var myVar = _Fetch(context, end, init, key)
console.log(myVar); // undefined
Similar questions that don't quite help in my case:
React - Can A Child Component Send Value Back To Parent Form
How to return values from function - React native

The correct way to solve this problem would be to use Redux or some other store manager. If you are using redux then you can update store with new values and it will be available to all the components. This way, your parent can access it directly.
fetch operation is async and it can not directly return data to the invoking method.
If you don't want to use redux then a very standard javascript solution will be using callback.
You can pass a callback method to your fetch function. In your code, you can replace return s with a call to callback method.
e.g. replace return s with callback(s).

Related

Call a function after state changes

I'm building a React Native app and when one button is pressed I want to call two functions. The first one will make a get call and set the state loading: true, the second one will show a popup with the result of that get call.
I am calling the second function only if loading === false but it is executed immediately after the first one before the state can change, because loading is false by default. I can resolve this with setTimeout but I was wondering if there was a cleaner way to do this.
onPress() {
this.props.getUsers();
setTimeout(() => {
if (this.props.loading === false) {
this.props.popUpVisible();
}
}, 1000);
}
You can create callback function for that
getUsers = (callback) => {
//do whatever you want
//when it's done
callback();
}
In onPress function
onPress = () => {
this.props.getUsers(() => {
if (this.props.loading === false) {
this.props.popUpVisible();
}
});
}
setState Function can take two param:
setState(updater, callback)
setState({loading:true},() => {
//this fires once state.loading === true
})
Use getDerivedStateFromProps. It always fire when component's props change.
Below is the example.
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}

Failed in retrieve data from AsyncStorage

I am a beginner at react-native.
I trying to retrieve data that stored from screen1.js in Screen2.js but I failed.
I have import Asyncstorage from react-native for both .js
This how I store variable from screenone.js :
class screenone extends Component {
state = {
oldpin: '000000',
newpin: '',
secpin: '',
};
onPressButton = () => {
if (this.state.newpin == this.state.secpin) {
this.setState(
{ oldpin: this.state.newpin },
async() => await this.storeData());
}
else {
ToastAndroid.show("Password Unmatched", ToastAndroid.SHORT);
}
}
storeData = async () =>{
const {oldpin} = this.state;
let pin : oldpin;
try{
await AsyncStorage.setItem('mypin',pin);
ToastAndroid.show("Password Changed", ToastAndroid.SHORT);
}
catch (err){
console.warn(err)
}}
....
This is how I trying to retrieve data in screentwo.js:
class screentwo extends Component {
constructor(props) {
super(props);
this.onComplete = this.onComplete.bind(this);
this.state = {
pin: ''
};
}
retrieveData = async (mypin) => {
try {
let value = await AsyncStorage.getItem(mypin);
if (value !== null) {
console.log(value);
this.setState({
pin: value})
}
} catch (error) {
console.warn(err)
}
}
onComplete(inputtedPin, clear) {
retrieveData();
if (inputtedPin !== this.state.pin) {
ToastAndroid.show("Incorrect Pin", ToastAndroid.SHORT);
clear();
} else {
ToastAndroid.show("Pin is correct", ToastAndroid.SHORT);
clear();
this.props.navigation.navigate("Dashboard");
}}
....
Error:
Reference Error: ReferenceError:Can't find variable:retrieveData
Am I using the right way to stored and retrieve data?
Any suggestion?
Thank you.
There are a couple of issues that I can see with your code.
Firstly the retrieveData() function. It is asynchronous and should be called with await also you are getting the error: Reference Error: ReferenceError:Can't find variable:retrieveData because you haven't used this
So ideally you should call it await this.retrieveData();
There are a few more issues with this function. You use the parameter mypin but don't seem to pass any parameter to the function when you call it. Fixing this issue you should call retreiveData() like this:
await this.retrieveData('mypin');
Or you could remove passing the paramater altogether, which I will show how to do in my refactor below.
Finally you call retreiveData every time you check the inputtedPin this isn't that efficient, it is asynchronous so it may take some time, and secondly it also takes time for the setState function to complete, which means that the state may not have updated in time when you go to check it against the inputtedPin, meaning that you are checking the inputtedPin against the wrong value.
Code Refactor
This is how I would refactor your component.
Refactor retrieveData so that it no longer takes a parameter and the key is hardcoded in the .getItem
In the componentDidMount get the value of the pin from AsyncStorage and save it to state.
Remove the retrieveData call from onComplete
Here is the refactor
retrieveData = async () => { // parameter have been removed
try {
let value = await AsyncStorage.getItem('mypin'); // notice I am now passing the key as a string not as a parameter
if (value !== null) {
console.log(value);
this.setState({ pin: value })
}
} catch (error) {
console.warn(err)
}
}
// here we call the refactored retrievedData which will set the state.
async componentDidMount () {
await this.retrieveData();
}
onComplete(inputtedPin, clear) {
// we remove the call to retrieveData as we have already gotten the pin in the componentDidMount
if (inputtedPin !== this.state.pin) {
ToastAndroid.show("Incorrect Pin", ToastAndroid.SHORT);
clear();
} else {
ToastAndroid.show("Pin is correct", ToastAndroid.SHORT);
clear();
this.props.navigation.navigate("Dashboard");
}
}
only replace
retrieveData();
to
this.retrieveData();
When you call async method from a caller method that method also become async Try prefix
async onComplete () { await this.retrieveData() }

redux-saga: eventChannel and listener which react callback return

In react-native backhandler listener react to callback function and act appropriately.
I need to read my store and depending on it, return true or false.
But I cant use select effect in normal function and I cant affect listener callback function from "watchBackButton" function.
export function* backButtonListen() {
return eventChannel(emitter => {
const backHandlerListener = BackHandler.addEventListener(
"hardwareBackPress",
() => {
emitter("back pressed");
}
);
return () => {
backHandlerListener.remove();
};
});
}
export function* watchBackButton() {
const chan = yield call(backButtonListen);
try {
while (true) {
let back = yield take(chan);
}
}
Since event channels are not bidirectional, I don't think there is a way to get some current state from saga to event channel using the select effect.
However, it is possible to access the store directly. There are multiple ways to get the store instance to the event channel. See my other answer here.
Using e.g. the context method you could do something like this:
// redux.js
...
const store = createStore(...);
sagaMiddleware.runSaga(rootSaga, {store});
// root-saga.js
export default function * rootSaga(context) {
yield setContext(context);
yield fork(watchBackButton);
}
// watch-back-button.js
export function* backButtonListen() {
const store = yield getContext('store');
return eventChannel(emitter => {
const backHandlerListener = BackHandler.addEventListener(
"hardwareBackPress",
() => {
emitter("back pressed");
return store.getState().foo === 'bar';
}
);
return () => {
backHandlerListener.remove();
};
});
}
export function* watchBackButton() {
const chan = yield call(backButtonListen);
try {
while (true) {
let back = yield take(chan);
}
}

Update Data from one to. another screen

hey guys I'm using a text field that can show my global variable value
when I update global variable ex:- global.xx++
it can't refresh when I click on it .how do I update data at once from many screen.. here's my ex
class A extend component
{
if(responseJson.responseCode == '200'){
++global.cartitems ;
Obj.onPress();
Obj.componentDidMount();
Alert.alert('SHOPPING','Item added successfully.',[{text: 'OK',},],{ cancelable: false })
}
class b extend component
{
<Text
style={{color:'white',textAlign:'center',fontSize:10}}
>{global.cartitems}</Text>
}
I believe what you meant is to pass a value from one screen to another.
You can either use AsyncStorage to keep the data inside your memory, or pass the data through props navigation
AsyncStorage example:
Store Data:
_storeData = async () => {
try {
await AsyncStorage.setItem('#MySuperStore:key', 'I like to save it.');
} catch (error) {
// Error saving data
}
}
Fetch Data:
_retrieveData = async () => {
try {
const value = await AsyncStorage.getItem('TASKS');
if (value !== null) {
// We have data!!
console.log(value);
}
} catch (error) {
// Error retrieving data
}
}
Passing data through props navigation:
First class:
<Button onPress = {
() => navigate("ScreenName", {name:'Jane'})
} />
Second class:
const {params} = this.props.navigation.state

Override/Intercept XMLHttpRequest response in all browsers

What do I want to achieve ?
I want to intercept the XMLHttpRequest and modify the response for some particular requests. (For ex. decrypt content and assign it to back response)
What I have done so far ?
Below code intercepts the request and modifies the response. It works in all browsers (Chrome, Firefox, Opera, Edge) except IE 11.
const dummySend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
const _onreadystatechange = this.onreadystatechange;
this.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status === 200 || this.status === 1223) {
// as response is read-only and configurable, make it writable
Object.defineProperty(this, 'response', {writable: true});
this.response = modifyResponse(this.response);
}
}
if (_onreadystatechange) {
_onreadystatechange.apply(this, arguments);
}
}
dummySend.apply(__self, arguments);
}
What is the Issue ?
All of that doesn't work only in IE 11, The Error thrown is 'TypeError: Assignment to read-only property is not allowed in strict mode'.
Can someone please help me with this ?
I could do it the other way, which is to have a dummy XMLHttpRequest object exposed to the original requester and then handle the actual XMLHttpRequest yourself. Please read code for more clarity.
let oldXMLHttpRequest = window.XMLHttpRequest;
// define constructor for XMLHttpRequest proxy object
window.XMLHttpRequest = function() {
let _originalXhr = new oldXMLHttpRequest();
let _dummyXhr = this;
function decryptResponse(actualResponse) {
return base64Decrypted = decrypt(response, secret);
}
_dummyXhr.response = null;
// expose dummy open
_dummyXhr.open = function () {
const _arguments = [].slice.call(arguments);
// do any url modifications here before request open
_dummyXhr._url = _arguments[1];
return _originalXhr.open.apply(_originalXhr, _arguments);
};
// expose dummy send
_dummyXhr.send = function () {
let _onreadystatechange = _dummyXhr.onreadystatechange;
_originalXhr.onreadystatechange = function() {
if (this.readyState === 4 && (this.status === 200 || this.status === 1223)) {
_dummyXhr.response = decryptResponse(this.response);
}
// call callback that was assigned on our object
if (_onreadystatechange) {
_onreadystatechange.apply(_dummyXhr, arguments);
}
}
_originalXhr.send.apply(_originalXhr, arguments);
};
// iterate all properties in _originalXhr to proxy them according to their type
// For functions, we call _originalXhr and return the result
// For non-functions, we make getters/setters
// If the property already exists on _dummyXhr, then don't proxy it
for (let prop in _originalXhr) {
// skip properties we already have - this will skip both the above defined properties
// that we don't want to proxy and skip properties on the prototype belonging to Object
if (!(prop in _dummyXhr)) {
// create closure to capture value of prop
(function(prop) {
if (typeof _originalXhr[prop] === "function") {
// define our own property that calls the same method on the _originalXhr
Object.defineProperty(_dummyXhr, prop, {
value: function() {return _originalXhr[prop].apply(_originalXhr, arguments);}
});
} else {
// define our own property that just gets or sets the same prop on the _originalXhr
Object.defineProperty(_dummyXhr, prop, {
get: function() {return _originalXhr[prop];},
set: function(val) {_originalXhr[prop] = val;}
});
}
})(prop);
}
}