Change text using hooks in react native - reactjs

I have a very simple question. I am using hooks, and in the docs there is an example given to change the text using hooks. But there is place where I am not able to figure it out.
Below is my code:
import React, { useState } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
const HelloWorldApp = () => {
const [textUpdate, setText] = useState('text1');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>{textUpdate}</Text>
<Text>{todos.text}</Text>
</View>
);
}
export default HelloWorldApp;
I can see text1 on the screen, But I cant see Learn hooks on the screen. What is wrong with <Text>{todos.text}</Text> ??
EDIT
So I changed <Text>{todos.text}</Text> to <Text>{todos[0].text}</Text>.
Now I wanted to change the text on the click.
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>{textUpdate}</Text>
<Text>{todos[0].text}</Text>
<TouchableOpacity onPress={changeText} >
<Text>Click me</Text>
</TouchableOpacity>
</View>
and also added changeText in the component.
const changeText = () => {
if (textUpdate === 'text1' || todos[0].text === 'Learn') {
setText('text2');
setTodos({ text: 'done' })
} else if (textUpdate === 'text2' || todos[0].text === 'done') {
setText('text1');
setTodos({ text: 'Learn' })
}
}
It gives an error undefined is not an object (evaluating 'todos[0].text')

UPDATED ISSUE
You have initialized state an array of object and reset it like object only
UPDATED SOLUTION
Try using as an array of an object like
const changeText = () => {
if (textUpdate === 'text1' || todos[0].text === 'Learn') {
setText('text2');
setTodos([{ text: 'done' }]) // add like array
} else if (textUpdate === 'text2' || todos[0].text === 'done') {
setText('text1');
setTodos([{ text: 'Learn' }]) // add like array
}
}

Related

How to save the QR Data and copy my clipboard?

I just have some question.
First, I'm beginner... Very sorry.
I have been developed the react-native app (iOS) for recognize the QR Code.
I already have been succeed the recognize the QR Code on my App.
But My Plan is..
First, I scan the QR Code and I want to save the QR data.
For the purpose, there are many QR Code in the warehouse above many box and it is included Serial Number.
After scanning, I will continue the scan until I want to stop it. (In conclusion, I scan many times.)
I just thought "Let's save the Serial Number at array.
Second, I save the serial number through the scanning and I want to copy our clipboard.
Also, I want to print in my App.
How can I implement this? I have no idea about that.
Code is here.
import { StatusBar } from 'expo-status-bar';
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function App() {
const [hasPermisson, setHasPermisson] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('Not yet scanned')
const askForCameraPermisson = () => {
(async () => {
const {status} = await BarCodeScanner.requestPermissionsAsync();
setHasPermisson(status == 'granted')
})()
}
useEffect(() => {
askForCameraPermisson ();
}, [])
const handleBarCodeScanned = ({type, data}) => {
setScanned(true);
setText(text);
state = {
sn : [
{
type : type,
data : data
},
{
type : type,
data : data
}
]
}
console.log(state)
}
if (hasPermisson === null) {
return (
<View style={styles.container}>
<Text>Requesting for camera permisson</Text>
<StatusBar style="auto" />
</View>
)
}
if(hasPermisson === false) {
return (
<View style={styles.container}>
<Text>No acess to camera</Text>
<Button title={'Allow Camera'} onPress={() => askForCameraPermisson()}/>
</View>
)
}
return (
<View style={styles.container}>
<View style={styles.barcodebox}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style = {{ height:400, width:400}} />
</View>
<Text style={styles.maintext}>{text}</Text>
{scanned && <Button title={'scan again?'} onPress={( ) => setScanned(false)} color='tomato'/>}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
barcodebox: {
alignItems: 'center',
justifyContent: 'center',
height: 300,
width:300,
overflow:'hidden',
borderRadius:30,
backgroundColor:'tomato'
},
maintext: {
fontSize :16,
margin:20
}
});

React Native how can I get last value of stateful array which has been updated by a spread operator?

I am updating a list (kind of like a todo list) and trying to persist it to AsyncStorage but the latest item added to array is always missing. Why?
Here is the offending function (shortened for clarification):
// At beginning of component
let [itemsArray, updateItemsArray] = useState([])
const addItem = async (item) => {
const currentItem = {
id: uuid(), // <-- temporary way of getting key for now
name: item.name
}
// Use spread operator to update stateful array for screen listing
// The listing on the screen updates perfectly with the 'new item' in place at the bottom
of a list
updateJobsArray(prevItems => [...prevItems, currentJob])
// Now, stringify the items array in preparation for saving to AsyncStorage
updateItemsArray(prevItems => [...prevItems, currentItem])
try {
const jsonValue = JSON.stringify(itemsArray)
await AsyncStorage.setItem('items', jsonValue)
} catch (e) {
Alert.alert('Error', 'Something went horribly, irrevocably... wrong')
}
}
When I console.log AsyncStorage.getItem('items'), the last item added is always missing from the resultant list of items. The items list is always missing the last added item. I think that the problem lies in the way the spread operator updates the stateful 'itemsArray'. It's like as if the state update is async and the write to AsyncStorage happens before the update is finished, but I can't find out why, please help...
I reproduce issue with working example, please test code at https://snack.expo.dev/#emmbyiringiro/c65dbb
import * as React from 'react';
import { Text, View, StyleSheet,Button,AsyncStorage,Alert,ScrollView } from 'react-native';
import Constants from 'expo-constants';
import faker from 'faker'
// You can import from local files
import AssetExample from './components/AssetExample';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default function App() {
let [itemsArray, updateItemsArray] = React.useState([])
let [savedUsers, updateSavedUsers] = React.useState([])
let [asyncOps,updateAsyncOps] = React.useState({saveStatus:"undetermined",retrieveStatus:"undetermined"})
const save = async ()=>{
const newUser ={
id:faker.datatype.uuid()
,
name: faker.name.findName(), // Rowan Nikolaus
email: faker.internet.email(),// Kassandra.Haley#erich.biz,
phone:faker.phone.phoneNumber(),
}
const tempUsers = [...itemsArray,newUser]
const serializeValues = JSON.stringify(itemsArray)
try {
updateAsyncOps({...asyncOps,saveStatus:"pending"})
await AsyncStorage.setItem('users', serializeValues)
await retrieve()
updateItemsArray(tempUsers)
updateAsyncOps({...asyncOps,saveStatus:"succeeded"})
} catch (e) {
updateAsyncOps({...asyncOps,saveStatus:"failed"})
Alert.alert('Error', 'Something went horribly, irrevocably... wrong')
}
}
const retrieve = async () => {
try {
updateAsyncOps({...asyncOps,retrieveStatus:"pending"})
const value = await AsyncStorage.getItem('users');
if (value !== null) {
// We have data!!
console.log(value);
const deSerializeValue = JSON.parse(value)
updateSavedUsers( deSerializeValue)
updateAsyncOps({...asyncOps,retrieveStatus:"suceeded"})
}
} catch (error) {
// Error retrieving data
Alert.alert('Error', 'Something went horribly, irrevocably... wrong')
updateAsyncOps({...asyncOps,retrieveStatus:"failed"})
}
};
return (
<ScrollView style={styles.container}>
<Card>
<View>
{ savedUsers.map(user=>{
return (
<View style={{paddingVertical:5}} key={user.id}>
<Text> { user.name} </Text>
<Text> { user.email} </Text>
<Text> { user.phone} </Text>
</View>
)
})}
</View>
<View style={{padding:10}}>
<Button onPress={save} title ='Add User ' disabled={asyncOps.saveStatus === 'pending'}/>
<View style={{paddingVertical:10}}>
<Button onPress={retrieve} title ='Retrieve Users ' disabled={asyncOps.retrieveStatus === 'pending'}/>
</View>
</View>
</Card>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});

setState not toggle value in react native

i am having function that toggle the state variables value.
the initial value of the state variable is false
Here is my function...
expandLists(label){ // "label" is a state variable that passed as a string
let result = new Boolean();
console.log(this.state);
if(this.state.label){
result=false;
console.log('Result = false');
}
else{
result=true;
console.log('Result = true');
}
this.setState({[label]: result},console.log(this.state))
}
In the above expression at inital state the value is changed to false then it is not changing to true.
I have also tried.. the below method...
expandLists(label){
this.setState( preState => ({label: !this.preState.label}),console.log(this.state))
}
If you pass the label parameter as a string, then try this:
expandLists(label){ // "label" is a state variable that passed as a string
let result = new Boolean();
console.log(this.state);
if(this.state[label]){
result=false;
console.log('Result = false');
}
else{
result=true;
console.log('Result = true');
}
this.setState({[label]: result},console.log(this.state))
}
So the difference is in checking if the current value is truethy. In stead of using this.state.label, use this.state[label].
Check this way as you said "label" param type of string
if(this.state.label == "true"){
...
}
or
if(this.state[label]){
...
}
Easy way to achieve this is
toggleLabelValue = (label) => {
this.setState({ [label]: !this.state[label] }, () =>
console.log(this.state)
);
};
Try toggling state in this way:
import React from 'react';
import {
View,
Button,
} from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
label: false
}
}
changeLabel = (currentLabel) => {
this.setState({
label: currentLabel
});
};
toggleLabel = () => {
this.changeLabel(!this.state.label);
};
render() {
return (
<View>
<Button onPress={this.toggleLabel} title="Toggle Label" />
</View>
);
}
}
Here is another implementation using hooks:
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
export default function App() {
const [label, setLabel] = useState(false);
const toggleLable = () => {
let temp = label;
setLabel(!temp);
};
return (
<View style={styles.container}>
<TouchableOpacity
onPress={toggleLable}
style={[
styles.btn,
{ backgroundColor: label ? '#4f4' : '#f40' },
]}>
<Text style={styles.text}>{label? "TRUE": "FALSE"}</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
btn: {
width: 200,
height: 200,
borderRadius: 20,
justifyContent: "center"
},
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
alignItems: 'center',
},
text:{
fontSize: 40,
fontWeight: "bold",
color: "white",
textAlign: "center"
}
});
Screenshot:
You can play around with the code here: Toggle Button Example
this works for me using useState:
import React, { useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import SeparateModal from 'components/SeparateModal';
export default function Parent() {
const [modalVisible, setModalVisible] = useState(false);
return (
<View>
<SeparateModal
modalVisible={modalVisible}
setModalVisible = {setModalVisible}
/>
<TouchableOpacity>
<Text onPress = { () => setModalVisible(true) }>Open Modal</Text>
</TouchableOpacity>
</View>
)
}
components/SeparateModal:
export default function SeparateModal({ modalVisible, setmodalVisible }) {
return (
<Modal
visible={ modalVisible }
animationType="slide"
>
<View>
<TouchableOpacity>
<Text onPress = { () => setModalVisible(false) }>Close Modal</Text>
</TouchableOpacity>
</View>
</Modal>
);

How to access next url from browser click from react-native-webview

I have a webview source that I have opened using react-native-webview, I want to access any and every url and console it whenever I click on the webview, so that I can use it as source for another webview. However am unable to figure how to do it
I tried using NavigationStateChange and onShouldLoadSTartwithrequest but that did not help.
below is my code
import React, {useState, useRef, useEffect} from 'react';
import {WebView} from 'react-native-webview';
import {
View,
SafeAreaView,
ActivityIndicator,
StyleSheet,
TouchableOpacity,
Text,
Linking,
Alert,
BackHandler,
} from 'react-native';
import Footer from '../components/Footer';
import {useBackHandler} from '#react-native-community/hooks';
import OnlineConsultationWebviewScreen from './OnlineConsultationWebviewScreen';
export default function ConsultationHomeScreen(props) {
const uri = props.route.params.uri;
const [canGoBack, setCanGoBack] = useState(false);
const [canGoForward, setCanGoForward] = useState(false);
const [currentUrl, setCurrentUrl] = useState('');
const webviewRef = useRef(null);
const renderLoadingView = () => {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<ActivityIndicator size="large" />
</View>
);
};
const onMessage = (e) => {
// retrieve event data
var data = e.nativeEvent.data;
// maybe parse stringified JSON
try {
data = JSON.parse(data);
} catch (e) {}
// check if this message concerns us
if ('object' == typeof data && data.external_url_open) {
// proceed with URL open request
return Alert.alert(
'External URL',
'Do you want to open this URL in your browser?',
[
{text: 'Cancel', style: 'cancel'},
{text: 'OK', onPress: () => Linking.openURL(data.external_url_open)},
],
{cancelable: false},
);
}
};
const jsCode = `!function(){var e=function(e,n,t){if(n=n.replace(/^on/g,""),"addEventListener"in window)e.addEventListener(n,t,!1);else if("attachEvent"in window)e.attachEvent("on"+n,t);else{var o=e["on"+n];e["on"+n]=o?function(e){o(e),t(e)}:t}return e},n=document.querySelectorAll("a[href]");if(n)for(var t in n)n.hasOwnProperty(t)&&e(n[t],"onclick",function(e){new RegExp("^https?://"+location.host,"gi").test(this.href)||(e.preventDefault(),window.postMessage(JSON.stringify({external_url_open:this.href})))})}();`;
return (
<SafeAreaView style={{flex: 1}}>
<WebView
source={{
uri: uri,
}}
renderLoading={renderLoadingView}
javaScriptEnabled={true}
domStorageEnabled={true}
startInLoadingState={true}
ref={webviewRef}
injectedJavaScript={jsCode}
onMessage={onMessage}
onError={console.error.bind(console, 'error')}
// onShouldStartLoadWithRequest={(event) => {
// if (event.url !== uri ){
// Linking.openURL(event.url);
// console.log('Event', event.url);
// return false;
// }
// return true;
// }}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
tabBarContainer: {
padding: 20,
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#b43757',
},
button: {
color: 'white',
fontSize: 24,
},
});
Am stuck with this since long, please let me know how do I access any and every click and console it, so that I can sue it later as a new source for Webview.
Any suggestion would be great.
Try this :
<WebView
ref="webview"
source={uri}
onNavigationStateChange={this._onNavigationStateChange.bind(this)}
javaScriptEnabled = {true}
domStorageEnabled = {true}
injectedJavaScript = {this.state.cookie}
startInLoadingState={false}
/>
Add this function :
_onNavigationStateChange(webViewState){
console.log(webViewState.url)
}
FYI webviewState object consists of, use the url property:
{
canGoBack: bool,
canGoForward: bool,
loading: bool,
target: number,
title: string,
url: string,
}
let me know if it helps

React Native Dynamic View Mapped from Array Does Not Update When Element Changes

I have a view in which I am dynamically adding Input components from React Native Elements. Ultimately, I need to validate the text typed in Input, and so, for now, I am trying to change the errorMessage prop from false to true upon onChangeText. Unfortunately, nothing I try changes the errorMessage even though the state component changes to true. I have a Snack, and, for convenience, my code follows:
import * as React from 'react'
import { ScrollView, StyleSheet, View } from 'react-native'
import { Button, Icon, Input, Text } from 'react-native-elements'
import * as Shortid from 'shortid'
export default class App extends React.Component {
constructor (props) {
super(props)
this.state = {
timeInputs: [],
validities: []
}
this.addTimeInput = this.addTimeInput.bind(this)
this.removeTimeInput = this.removeTimeInput.bind(this)
this.mutateValidities = this.mutateValidities.bind(this)
}
componentDidMount () {
this.addTimeInput()
}
addTimeInput () {
const identifier = Shortid.generate()
const timeInputs = this.state.timeInputs
const validities = this.state.validities
let isTimeValid = false
let time = ''
new Promise(
(resolve, reject) => {
resolve(
validities.push(
{ id: identifier, value: isTimeValid }
),
timeInputs.push(
<View key = { identifier }>
<View style = { styles.row }>
<Input
errorMessage = {
this.state.validities.filter(
validity => validity.id !== identifier
).value == true
? 'Valid'
: 'Invalid'
}
errorStyle = { styles.error }
onChangeText = {
value => {
time = value
this.mutateValidities(identifier, true)
console.log('TIME ' + identifier + ': ' + time)
}
}
placeholder = 'HH:MM AM/PM'
ref = { React.createRef() }
/>
<Icon
color = { colors.dark }
name = 'add-circle'
onPress = { () => this.addTimeInput() }
type = 'material'
/>
<Icon
color = { colors.dark }
name = 'remove-circle'
onPress = {
() => {
if (this.state.timeInputs.length > 1) {
this.removeTimeInput(identifier)
} else {
console.log('LENGTH: ' + this.state.timeInputs.length)
}
}
}
type = 'material'
/>
</View>
</View>
)
)
}
)
.then(
this.setState(
{
timeInputs: timeInputs,
validities: validities
}
)
)
.catch(
(reason) => {
console.log(
'Failed to create time-input because of the following: ' + reason
)
}
)
}
mutateValidities (key, value) {
this.setState(
{
validities: this.state.validities.map(
validity => validity.id === key
? {...validity, value: value}
: validity
)
}
)
}
removeTimeInput (key) {
this.setState(
{
timeInputs: this.state.timeInputs.filter(
timeInput => timeInput.key !== key
),
validities: this.state.validities.filter(
validity => validity.id !== key
)
}
)
}
render () {
return (
<ScrollView contentContainerStyle = { styles.container }>
<Text h4 style = { styles.title }>Time Inputs</Text>
{
this.state.timeInputs.map(
(value) => { return value }
)
}
<Button
buttonStyle = { styles.button }
onPress = {
() => this.state.validities.map(
validity => console.log(validity.id + validity.value)
)
}
title = 'Log Validities'
/>
</ScrollView>
)
}
}
const colors = {
dark: 'steelblue',
light: 'aliceblue',
medium: 'lightsteelblue',
error: 'firebrick'
}
const styles = StyleSheet.create(
{
button: {
backgroundColor: colors.dark,
margin: 5
},
container: {
alignItems: 'center',
backgroundColor: colors.light,
flex: 1,
justifyContent: 'center'
},
error: {
color: colors.error,
fontSize: 12,
margin: 5
},
row: {
alignItems: 'center',
flexDirection: 'row',
margin: 5,
width: '80%'
},
title: {
margin: 5
}
}
)
Due credit goes to McGregor (2017) for getting me this far, but I am still stuck.
Reference:
McGregor, L. (2017, October 2) Whats the best way to update an object in an array in ReactJS? [Stack Overflow answer]. Retrieved from https://stackoverflow.com/a/46518653/6084947
I believe my problem was that I was trying to trigger changes in the view that were too deeply nested. Even assuming the props were passed correctly, the view would not have updated when the state changed because React only compares props and state so far. Following the recommendation made in "The Power Of Not Mutating Data" (Facebook, 2019), I avoided these issues with shallow comparison by making the text-input a full React component. I also applied React Redux to manage the state in a central store instead of passing props back and forth, which I never could get to work even after abstracting the text-input.
The code is now spread out between too many different files to post here, but anyone interested can view it on GitHub or Snack.
Reference:
Facebook. (2019). Optimizing Performance [Documentation]. Retrieved from https://reactjs.org/docs/optimizing-performance.html#the-power-of-not-mutating-data

Resources