Return result of VersionCheck.getLatestVersion() as string - react-native

I'm trying to output the version number of my app using react-native-version-check, am I missing something here? Console logging latestVersion works but not this.
renderAppVersion = () => {
VersionCheck.getLatestVersion({
provider: 'appStore'
})
.then(latestVersion => {
return latestVersion;
});
}
<Text>App Version: {this.renderAppVersion()}</Text>

VersionCheck.getLatestVersion is an Async function so you wont get value when you use this.renderAppVersion() because it is also an Async function use state for storing the version which got from function and render it
code:
const App = () => {
const [version, setVersion] = useState();
useEffect(() => {
VersionCheck.getLatestVersion().then(v => setVersion(v))
}, [])
return (
<View>
{version && <Text>{version}</Text>}
</View>
);
}

Related

Updating useState array from callback

I am building a React Native (Expo) app that scans for Bluetooth devices. The Bluetooth API exposes a callback for when devices are detected, which I use to put non-duplicate devices into an array:
const DeviceListView = () => {
const [deviceList, setDeviceList] = useState([]);
const startScanning = () => {
manager.startDeviceScan(null, null, (error, device) => {
// Add to device list if not already in list
if(!deviceList.some(d => d.device.id == device.id)){
console.log(`Adding ${device.id} to list`);
const newDevice = {
device: device,
...etc...
};
setDeviceList(old => [...old, newDevice]);
}
});
}
// map deviceList to components
componentList = deviceList.map(...);
return <View> {componentList} </View>
}
The problem is that the callback is called many many times faster than setDeviceList updates, so the duplicate checking doesn't work (if I log deviceList, it's just empty).
If I use an additional, separate regular (non-useState) array, the duplicate checking works, but the state doesn't update consistently:
const DeviceListView = () => {
const [deviceList, setDeviceList] = useState([]);
var deviceList2 = [];
const startScanning = () => {
manager.startDeviceScan(null, null, (error, device) => {
// Add to device list if not already in list
if(!deviceList2.some(d => d.device.id == device.id)){
console.log(`Adding ${device.id} to list`);
const newDevice = {
device: device,
...etc...
};
deviceList2.push(newDevice);
setDeviceList(old => [...old, newDevice]);
}
});
}
// map deviceList to components
componentList = deviceList.map(...);
return <View> {componentList} </View>
}
This code almost works, but the deviceList state doesn't update correctly: it shows the first couple of devices but then doesn't update again unless some other component causes a re-render.
What do I need to do to make this work as expected?
I would suggest wrap your duplicate check within the state set function itself, and then return the same device list if no new devices have been found. This offloads race condition handling to the underlying react implementation itself, which I've found to be good enough for most cases.
Thus it would look something like this:
const DeviceListView = () => {
const [deviceList, setDeviceList] = useState([]);
const startScanning = () => {
manager.startDeviceScan(null, null, (error, device) => {
// Add to device list if not already in list
setDeviceList(old => {
if(!old.some(d => d.device.id == device.id)){
console.log(`Adding ${device.id} to list`);
const newDevice = {
device: device,
// ...etc...
};
return [...old, newDevice]
}
return old
});
});
}
// map deviceList to components
componentList = deviceList.map(...);
return <View> {componentList} </View>
}
Since old is unchanged if no new unique devices are found it will also skip next re-render according to the docs ( which is a neat optimisation :) )
This is the preferred way to implement state updates that are dependant on previous state according to the docs
https://reactjs.org/docs/hooks-reference.html#functional-updates
convert your callback to promise so that until you get completed device list, checkout below code (PS. not tested, please change as you need)
const [deviceList, setDeviceList] = useState([]);
const [scanning, setScanning] = useState(false);
useEffect(() => {
if(scanning) {
setDeviceList([]);
startScanning();
}
}, [scanning]);
const subscription = manager.onStateChange(state => {
if (state === "PoweredOn" && scanning === false) {
setCanScan(true);
subscription.remove();
}
}, true);
const fetchScannedDevices = () => {
return new Promise((resolve, reject) => {
manager.startDeviceScan(null, null, (error, device) => {
// Add to device list if not already in list
if (!deviceList.some(d => d.device.id == device.id)) {
console.log(`Adding ${device.id} to list`);
const newDevice = {
device: device,
// ...etc...
};
resolve(newDevice);
}
if (error) {
reject({});
}
});
});
};
const startScanning = async () => {
try {
const newDevice = await fetchScannedDevices();
setDeviceList(old => [...old, newDevice]);
} catch (e) {
//
}
};
const handleScan = () => {
setScanning(true);
};
// map deviceList to components
componentList = deviceList.map(() => {});
return (
<View>
<Button
onPress={() => handleScan()}>
Scan
</Button>
<View>{componentList}</View>
</View>
);
};

TypeError: undefined is not an object (evaluating 'ingredients.map')

Ingredients should be an object containing a list of ingredients with properties. But when using ingredients.map, it returns the " undefined is not an object( evaluating 'ingredients.map')" error and makes the console.log(recipeData2 suddenly does not return anything anymore).
Why is this going wrong and how could I achieve my goal? I am trying to print the name of every ingredient in the ingredients.
export default function DetailPage ({navigation, route} : any) {
const [recipeData2, setrecipeData2] = useState<Recipe_detail[] | null>([]);
const [loading, setLoading] = useState<boolean>(true);
const [key, setKey] = useState<string>('API KEY HERE');
const {id} = route.params;
const getrecipeData2 = async() => {
setLoading(true);
const url = 'https://api.spoonacular.com/recipes/' + id + '/information?apiKey=' + key + '&includeNutrition=false';
axios.get(url).then(function (response) {
setrecipeData2(response.data);
}).catch(function (error) {
if(error) console.log('error')
}).finally(function () {
setLoading(false);
})
}
console.log(recipeData2);// this suddenly returns undefined when the .map function is added to the code
useEffect(() => {
getrecipeData2();
}, [id])
function handleOnPress() {
setrecipeData2(null)
navigation.navigate('Home')
}
const ingredients = recipeData2?.extendedIngredients ;
console.log(ingredients);// this returns what i need so i know it works
//this is the function in question that breaks everything
const ingredientsJSX = ingredients.map((item, index) => {
})
return (
<View>
<Button title="back" onPress={handleOnPress}/>
<Text>{recipeData2?.title}</Text>
<Text>{recipeData2?.summary}</Text>
<Text>{recipeData2?.instructions}</Text>
</View>
)
}

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

React Native - How to use Promises with async

I'm pretty new of RN but I don't really understand Promises with async.
I got how works but I don't use them correctly cause they don't work in my code.
Example
const getAnimationTime = () => {
let res = meditationTime;
return new Promise(resolve => resolve(res));
};
and then:
useEffect(() => {
(async function fetchData() {
const fetcher = await getAnimationTime();
console.log('fetcher', fetcher);
setAnimationTime(fetcher);
})();
console.log('anitime useEffect', animationTime);
}, []);
I did what I saw in tutorials and doesn't work. In this case, I always get undefined
Can someone explain to me how works?
Thanks!
Try to move variable declared inside the Promise block.
const getAnimationTime = () => {
return new Promise((resolve, reject) => {
let res = 100;
resolve(res);
})
};
I solved like adding this:
if (isLoading || animationTime === null) {
return <PulseIndicator color="green" size={50} />;
} else {
return (
<TouchableOpacity
onPress={handlePause}
onLongPress={longPress}
style={styles.container}>
...
It simply waits for the changing state

React Native Not Updating useState with setInterval

L01 should always be either Yes or No based on a query result returned. It starts out as 'No' and then as as soon as the query is checked it receives 'Yes'. However my return View does not update to Yes even though the setLevel01 value of it clearly shows 'Yes' in the console log. Any thoughts?
function DashScreen() {
let [L01, setLevel01 ] = useState('No');
useEffect(() => {
const timer = setInterval(() => (
setLevel01 = data.getlevels[0].level01,
console.log("inside: ",setLevel01)
), 1000);
return () => {
clearInterval(timer);
}
});
const { loading, error, data } = useQuery(GetLevelsQuery)
if (loading) return 'Loading...'
if (error) return 'Something Bad Happened'
console.log("outside: ",L01);
return (
<View><Text>{L01}</Text></View>
);}
setLevel01 is a function use to set the new state value. Try this:
function DashScreen() {
const [L01, setLevel01 ] = useState('No');
useEffect(() => {
const timer = setInterval(() => (
setLevel01(data.getlevels[0].level01)
), 1000);
return () => {
clearInterval(timer);
}
});
const { loading, error, data } = useQuery(GetLevelsQuery)
if (loading) {
return <Text>'Loading...'</Text>;
}
else if (error) {
return <Text>'Something Bad Happened'</Text>;
}
return (
<View>
<Text>{L01}</Text>
</View>
);
}