Cannot Store and Retrieve data using AsyncStorage in React native - react-native

I am new to react native,I was just learning react native now,i was trying to store values from TextInput and store into AsyncStorage by clicking Register Button.I am trying to get that value by clicking Get Button.I can't get value from AsyncStorage.getItem('value'), later I was thought wether data is storing in AsyncStorage.setItem('name','value').Somebody look at this code and tell me the answer where i was made mistake.
export default class AsyncStorageEample extends Component
{
constructor(props)
{
super(props)
this.state= { name:'', email:'',mobile:''}
this.onRegisterPress=this.onRegisterPress.bind(this)
this.onRetreive=this.onRetreive.bind(this)
}
onRegisterPress()
{
let name=this.state.name
let email=this.state.email
let mobile=this.state.mobile
AsyncStorage.setItem('name',name)
AsyncStorage.setItem('email',email)
AsyncStorage.setItem('mobile',mobile)
this.setState({name: name, email:email,mobile:mobile})
}
onRetreive()
{
var user;
user= AsyncStorage.getItem('name').then((value) => this.setState({ 'name': value }))
Alert.alert('Name:'+user);
}
render() {
return (
<View style={styles.container}>
<View style={styles.Signupform}>
<Text style={styles.textStyle}> Name </Text>
<TextInput style={styles.tInRegister} onChangeText={(text)=> this.setState({name: text})} value = {this.state.name}/>
<Text style={styles.textStyle}> Email </Text>
<TextInput style={styles.tInRegister} onChangeText={(text)=> this.setState({email: text})} value= {this.state.email} />
<Text style={styles.textStyle}> Mobile </Text>
<TextInput style={styles.tInRegister} onChangeText={(text)=> this.setState({mobile: text})} vaue= {this.state.mobile} />
<Button
onPress={this.onRegisterPress.bind(this)}
title="Register"
color="#841584"
/>
<Button
style={styles.get}
onPress={this.onRetreive.bind(this)}
title="Get"
color="#841584"/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
tInRegister: {
fontSize: 16,
height: 40,
color: '#800080'
},
textStyle: {
fontSize: 16,
color: '#800080'
},
Signupform: {
width: 400,
height: 100,
justifyContent: 'center',
marginTop: 10,
marginLeft: 10,
marginRight: 10
},
get:
{
marginTop:20,
},
});
AppRegistry.registerComponent('AsyncStorageEample', () => AsyncStorageEample);

AsyncStorage is an asynchronous operation. So it should be done with 'async - await' .
Try the following code
async onRetreive() {
const user = await AsyncStorage.getItem('name');
Alert.alert('Name:'+user);
}

Related

React Native - onPress not changing radio button style

When i click my Pressable RadioButton i want it to change the button style from styles.radio to styles.checked. so that it fills the empty circle. How can i implement this so that it happens when user is pressing my RadioButton? Right not nothing happens on the click, just gettin the console message.
type RadioProps = {
checked: boolean;
onPress: () => void;
disabled?: boolean;
checked: boolean;
};
export function RadioButton({ checked, disabled, onPress }: RadioProps) {
return (
<Pressable
style={styles.container}
onPress={onPress}
disabled={disabled}
>
<View style={[styles.radio, checked && styles.checked]}></View>
<Text style={styles.text}>
Label
</Text>
</Pressable>
);
}
const styles = StyleSheet.create({
radio: {
width: 24,
height: 24,
borderRadius: 12,
borderWidth: 1,
borderColor: "gray",
},
checked: {
borderColor: "red",
backgroundColor: "white",
borderWidth: 3,
justifyContent: "center",
alignItems: "center",
},
container: {
flexDirection: "row",
},
text: {
marginLeft: 16,
color: theme.color.gray3,
},
});
App.tsx file
export default function App() {
return (
<View>
<RadioButton
onPress={() => {
console.log("pressed");
}}
checked={checked}
/>
</View>
);
}
You are not changing the checked state which is defined in App. You need to change the state or nothing will happen.
export default function App() {
const [checked, setChecked] = useState(false);
return (
<View>
<RadioButton
onPress={() => {
console.log("pressed");
setChecked(prev => !prev);
}}
checked={checked}
/>
</View>
);
}

Why is the checkbox not setting checked and why does console.log is called multiple times?

Here is my Login screen
import {
View,
StyleSheet,
Text,
TouchableWithoutFeedback,
ScrollView,
Platform,
KeyboardAvoidingView,
Alert,
Image,
TouchableOpacity,
} from "react-native";
import * as LocalAuthentication from "expo-local-authentication";
import { CustomCheckBoxInput, CustomTextInput } from "#components/Input";
import { useForm } from "react-hook-form";
import { Ionicons } from "#expo/vector-icons";
import colors from "#config/colors";
import { AppButton } from "#components/Button";
import AppScreen from "#components/AppScreen";
import { loginUser } from "#redux/slices/auth";
import { useCallback, useEffect, useState, memo, useMemo } from "react";
import { useAppDispatch, useAppSelector } from "#hooks/hooks";
import useAuth from "#hooks/useAuth";
import { getItem, removeItem, setItem } from "#config/dataStorage";
import { useFocusEffect, useIsFocused } from "#react-navigation/native";
interface LoginScreenProps {}
const LoginScreen = ({ navigation }: any) => {
const [data, setData] = useState(null);
const { loading, logIn } = useAuth();
const { control, handleSubmit, reset } = useForm();
const focused = useIsFocused();
const getData = async () => {
const data = await getItem("#user_data");
const parsedData = JSON.parse(data);
if (parsedData) setData(parsedData);
};
useEffect(() => {
if (focused) {
getData();
const defaults = {
email: data?.email,
remember: data?.isRemembered,
};
reset(defaults);
}
}, [reset, data?.email, focused]);
console.log(data);
const onSubmit = useCallback(async (data) => {
const response = await logIn(data);
if (data.remember && response.success) {
await setItem(
"#user_data",
JSON.stringify({ email: data.email, isRemembered: data.remember })
);
} else {
await removeItem("#user_data");
}
}, []);
const onFaceId = async () => {
try {
// Checking if device is compatible
const isCompatible = await LocalAuthentication.hasHardwareAsync();
if (!isCompatible) {
throw new Error("Your device isn't compatible.");
}
// Checking if device has biometrics records
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
if (!isEnrolled) {
throw new Error("No Faces / Fingers found.");
}
// Authenticate user
await LocalAuthentication.authenticateAsync();
Alert.alert("Authenticated", "Welcome back !");
} catch (error) {
Alert.alert("An error as occured", error?.message);
}
};
return (
<AppScreen>
<KeyboardAvoidingView
style={{
flex: 1,
paddingHorizontal: 20,
}}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<Image
source={require("assets/logo5.png")}
resizeMode="contain"
style={{ position: "absolute", top: 0, left: 0 }}
/>
<ScrollView>
<View
style={{
flexDirection: "column",
marginTop: "10%",
marginBottom: "3%",
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
}}
>
<TouchableWithoutFeedback
onPress={() => navigation.navigate("Welcome")}
>
<Ionicons
name="chevron-back-outline"
size={30}
color="#fff"
style={{ marginStart: -10 }}
/>
</TouchableWithoutFeedback>
<Text style={{ color: "white", fontSize: 32 }}>
Welcome back,
</Text>
</View>
</View>
<Text
style={{
color: "white",
fontSize: 18,
fontWeight: "200",
marginBottom: "5%",
}}
>
Please login to your account to continue.
</Text>
<Text style={{ color: "#fff" }}>{data?.email}</Text>
<Text style={{ color: "#fff" }}>{data?.isRemembered.toString()}</Text>
<CustomTextInput
name="email"
control={control}
placeholder="email"
value={data?.email}
textContentType="emailAddress"
label="E-mail"
rules={{ required: "This field is required!" }}
/>
<CustomTextInput
name="password"
control={control}
placeholder="password"
label="Password"
rules={{ required: "This field is required!" }}
secureTextEntry={true}
/>
<CustomCheckBoxInput
control={control}
name="remember"
label="Remember Me"
isChecked={data?.isRemembered}
/>
<AppButton
title="Login"
bgColor={colors.buttonPrimary}
loading={loading}
onPress={handleSubmit(onSubmit)}
style={{ marginTop: 10, marginBottom: 10 }}
/>
<View
style={{ flexDirection: "row", justifyContent: "space-between" }}
>
<TouchableOpacity
activeOpacity={0.7}
style={{ marginBottom: 20, alignSelf: "flex-end" }}
onPress={onFaceId}
>
<Text
style={{
color: colors.buttonPrimary,
fontWeight: "300",
fontSize: 16,
}}
>
Login with Face ID
</Text>
</TouchableOpacity>
<TouchableOpacity
activeOpacity={0.7}
style={{ marginBottom: 20, alignSelf: "flex-end" }}
onPress={() => navigation.navigate("Email")}
>
<Text
style={{
color: colors.smallTextHex,
fontWeight: "600",
fontSize: 16,
}}
>
Forgot Password
</Text>
</TouchableOpacity>
</View>
<View
style={{
flexDirection: "row",
justifyContent: "center",
}}
>
<Text style={{ color: "#fff", fontSize: 16, fontWeight: "200" }}>
New to TickersFund?{" "}
</Text>
<TouchableOpacity
onPress={() => navigation.navigate("Register")}
activeOpacity={0.7}
>
<Text
style={{
color: colors.smallTextHex,
fontWeight: "600",
color: colors.buttonPrimary,
fontSize: 16,
}}
>
Register Here
</Text>
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
</AppScreen>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: colors.background,
// padding: 10,
},
top_container: {},
});
export default LoginScreen;
What I wanted is showing defaultValue for Email and Checkbox if user submits data with remember true
Problem
Email showing but checkbox is not showing checked
multiple console.log() showing
react-native-async-storage: ~1.15
Why am I seeing multiple console.logs and why value is not setting in the CustomCheckBox
export const CustomCheckBoxInput = ({
control,
name,
label,
...props
}: CustomChecboxProps) => {
return (
<Controller
control={control}
name={name}
render={({
field: { value, onChange, onBlur },
fieldState: { error },
}) => (
<BouncyCheckbox
onPress={(value) => {
Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
onChange(value);
}}
fillColor={colors.buttonPrimary}
size={18}
unfillColor={colors.background}
style={{ marginTop: 10, marginLeft: 5 }}
text={label}
textStyle={{ fontSize: 18, textDecorationLine: "none" }}
iconStyle={{ borderColor: colors.buttonPrimary, marginRight: -5 }}
{...props}
/>
)}
/>
);
};
Here is my CustomCheckBox component

Component cannot read JSON properties

I am trying to figure out why my components are not rendering without errors. I am trying to parse a JSON file that has been saved into a redux store but whenever I call try to reference the redux state through props such as this.props.response.data.specificJSONObject. I will instead get an error that states cannot read property of 'specificJSONObject' of undefined. This problem has plagued all of my components that need the use of the JSON file in the redux store. I initially found that my redux store was not updating(asked question: Redux state not being updated after dispatched actions) when making dispatches but have fixed that and now my redux store is able to store the fetched data it needs but I still get the same error as before.
This is my Summary.js screen for Minimal reproducible code
import React, { Component } from "react";
import {
View,
ImageBackground,
StyleSheet,
Image,
Text,
FlatList
} from "react-native";
import { connect } from "react-redux";
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
import PropTypes from "prop-types";
import LinkedName from "./LinkedName";
import ArticleBox from "./ArticleBox";
const styles = StyleSheet.create({
container: {
backgroundColor: "dimgrey",
flex: 1
},
posLeft: {
alignItems: "center",
position: "absolute",
top: 40,
left: 25
},
posRight: {
alignItems: "center",
position: "absolute",
top: 40,
left: 175
},
text: {
textAlign: "center",
color: "#FFF"
},
header_text: {
textAlign: "center",
fontSize: 20,
color: "#FFF"
},
image: {
position: "absolute",
top: 200,
height: 375,
width: 375,
flex: 1
},
topPanel: {
backgroundColor: "rgba(0.0,0.0,0.0,0.9)",
height: 150,
width: 300,
position: "absolute",
alignSelf: "center",
flex: 2,
flexDirection: "column",
borderRadius: 25,
borderWidth: 4
},
midPanel: {
backgroundColor: "rgba(0.0,0.0,0.0,0.9)",
height: 100,
width: 300,
position: "absolute",
bottom: 120,
alignSelf: "center",
flex: 2,
flexDirection: "column",
borderRadius: 25,
borderWidth: 4
}
});
class Summary extends Component<Props> {
constructor(props) {
super(props);
this.state = {
taxonA: this.props.response.articles.taxon_a,
taxonB: this.props.response.articles.taxon_b,
sciNameA: this.props.response.articles.scientific_name_a,
sciNameB: this.props.response.articles.scientific_name_b,
hitRecords: Object.keys(this.props.response.articles.hit_records).map(
key => ({ key, ...this.props.response.articles.hit_records[key] })
),
TTOL:
Math.round(this.props.response.articles.sum_simple_mol_time * 10) / 10,
median: Math.round(this.props.response.articles.sum_median_time * 10) / 10
};
// console.log(STORE, this.props.response.articles);
}
componentDidMount = () => {
console.log("STATE", this.state);
};
render() {
return (
<View style={styles.container}>
<ImageBackground
source={require("../assets/images/timescale.png")}
style={styles.image}
resizeMode="contain"
alignSelf="center"
>
<View style={styles.topPanel}>
<Text style={styles.header_text}>Query Taxa</Text>
<View style={styles.posLeft}>
<Text
style={{ textAlign: "center", color: "#FFF", fontSize: 17 }}
>
Taxon A
</Text>
<Text />
<Text style={styles.text}>{this.state.taxonA}</Text>
<Text />
<LinkedName
url={this.state.hitRecords[0].link_taxon_a}
latinName={this.state.sciNameA}
/>
</View>
<View style={styles.posRight}>
<Text
style={{ textAlign: "center", color: "#FFF", fontSize: 17 }}
>
Taxon B
</Text>
<Text />
<Text style={styles.text}>{this.state.taxonB}</Text>
<Text />
<LinkedName
url={this.state.hitRecords[0].link_taxon_b}
latinName={this.state.sciNameB}
/>
</View>
</View>
<View style={styles.midPanel}>
<Text style={styles.header_text}>Result</Text>
<Text />
<Text style={styles.text}>TTOL: {this.state.TTOL} MYA</Text>
<Text />
<Text style={styles.text}>median: {this.state.median} MYA</Text>
</View>
<View style={{ position: "absolute", bottom: -35, marginLeft: -5 }}>
<FlatList
horizontal
data={this.state.hitRecords}
renderItem={({ item }) => {
return (
<ArticleBox
title={item.title}
year={item.year}
time={item.time}
author={item.author}
/>
);
}}
itemSeparatorComponent={() => (
<View
style={{
backgroundColor: "#ff8c00",
height: 100,
width: 100
}}
/>
)}
/>
</View>
</ImageBackground>
</View>
);
}
}
Summary.propTypes = {
fetchData: PropTypes.func.isRequired,
response: PropTypes.object.isRequired
};
const mapStateToProps = state => {
return { response: state };
};
const mapStateToDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url))
});
export default connect(
mapStateToProps,
mapStateToDispatch
)(Summary);
This is the function where I reference the Redux state through props
constructor(props) {
super(props);
this.state = {
taxonA: this.props.response.articles.taxon_a,
taxonB: this.props.response.articles.taxon_b,
sciNameA: this.props.response.articles.scientific_name_a,
sciNameB: this.props.response.articles.scientific_name_b,
hitRecords: Object.keys(this.props.response.articles.hit_records).map(
key => ({ key, ...this.props.response.articles.hit_records[key] })
),
TTOL:
Math.round(this.props.response.articles.sum_simple_mol_time * 10) / 10,
median: Math.round(this.props.response.articles.sum_median_time * 10) / 10
};
// console.log(STORE, this.props.response.articles);
}
In this Instance, I built it right when the state was built but in other components, I will set the state through the componentWillMount function and the problem will still persist.
My expected output should be no errors and my components render properly. The received output is the red screen will the error message cannot read property of 'specificJSONObject' of undefined.
The Answer in the question linked up top helps to solve this problem, but will explained here as well.
In my Summary Screen, the problem is being mapped to response. If you see the MapStatetoProps functions is written like this
const mapStateToProps = state => {
return { response: state };
};
but should be written like this
const mapStateToProps = state => {
return { response: state.fetchingStatus };
};
due to how my Redux store is set up and dispatches(can be seen in the question linked above)

How to snap pictures using expo react native camera?

I have just started using React Native with Expo so I am kind of confused. So, I have made a camera component which I imported in the main screen. Everything looks good. But I can't take pictures. I cannot click the snap icon and save the image. Is there a component that I missed?
I have only posted the CameraComponent class below.
Camera.js
class CameraComponent extends Component {
state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back
}
async componentWillMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ hasCameraPermission: status === 'granted' })
}
render() {
const { hasCameraPermission } = this.state
if (hasCameraPermission === null) {
return <View />
}
else if (hasCameraPermission === false) {
return <Text> No access to camera</Text>
}
else {
return (
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1, justifyContent: 'space-between' }}
type={this.state.type}
>
<Header
searchBar
rounded
style={{
position: 'absolute',
backgroundColor: 'transparent',
left: 0,
top: 0,
right: 0,
zIndex: 100,
alignItems: 'center'
}}
>
<View style={{ flexDirection: 'row', flex: 4 }}>
<Ionicons name="md-camera" style={{ color: 'white' }} />
<Item style={{ backgroundColor: 'transparent' }}>
<Icon name="ios-search" style={{ color: 'white', fontSize: 24, fontWeight: 'bold' }}></Icon>
</Item>
</View>
<View style={{ flexDirection: 'row', flex: 2, justifyContent: 'space-around' }}>
<Icon name="ios-flash" style={{ color: 'white', fontWeight: 'bold' }} />
<Icon
onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back ?
Camera.Constants.Type.front :
Camera.Constants.Type.back
})
}}
name="ios-reverse-camera"
style={{ color: 'white', fontWeight: 'bold' }}
/>
</View>
</Header>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 30, marginBottom: 15, alignItems: 'flex-end' }}>
<Ionicons name="ios-map" style={{ color: 'white', fontSize: 36 }}></Ionicons>
<View></View>
<View style={{ alignItems: 'center' }}>
<MaterialCommunityIcons name="circle-outline" // This is the icon which should take and save image
style={{ color: 'white', fontSize: 100 }}
></MaterialCommunityIcons>
<Icon name="ios-images" style={{ color: 'white', fontSize: 36 }} />
</View>
</View>
</Camera>
</View>
)
}
}
}
export default CameraComponent;
The icon in the center i;e circle icon should automatically take and save image.
You can use "onPictureSaved" when the asynchronous takePictureAsync function returns so that you can grab the photo object:
takePicture = () => {
if (this.camera) {
this.camera.takePictureAsync({ onPictureSaved: this.onPictureSaved });
}
};
onPictureSaved = photo => {
console.log(photo);
}
In the view you would have a Camera component that has a ref:
<Camera style={styles.camera} type={this.state.type} ref={(ref) => { this.camera = ref }} >
As well as a button that will call the takePicture function on press:
<TouchableOpacity style={styles.captureButton} onPress={this.takePicture} />
So you need to tell your 'circle icon' to take the picture. First I would add a reference to your camera like so
<Camera style={{ flex: 1 }}
ref={ (ref) => {this.camera = ref} }
type={this.state.type}>
then create a function that actually tells your app to take the photo:
async snapPhoto() {
console.log('Button Pressed');
if (this.camera) {
console.log('Taking photo');
const options = { quality: 1, base64: true, fixOrientation: true,
exif: true};
await this.camera.takePictureAsync(options).then(photo => {
photo.exif.Orientation = 1;
console.log(photo);
});
}
}
Now make your icon have an onPress() to take the photo. I did something like this.
<TouchableOpacity style={styles.captureButton} onPress={this.snapPhoto.bind(this)}>
<Image style={{width: 100, height: 100}} source={require('../assets/capture.png')}
/>
</TouchableOpacity>
You may also want to create a view that renders an image preview or something similar. The Expo documentation has a fairly good example on getting started. Note that Expo creates a cached folder called 'Camera' and that's where the image initially is.
You can do this in a functional component as well using a ref created via a React Hook. Here is an example based on the expo SDK 38 Camera component https://docs.expo.io/versions/v38.0.0/sdk/camera/
import React, { useState, useEffect, useRef } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const ref = useRef(null)
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
_takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={type} ref={ref}>
<View
style={{
flex: 1,
backgroundColor: 'transparent',
flexDirection: 'row',
}}>
<TouchableOpacity
style={{
flex: 0.1,
alignSelf: 'flex-end',
alignItems: 'center',
}}
onPress={() => {
setType(
type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back
);
}}>
<Text style={{ fontSize: 18, marginBottom: 10, color: 'white' }}> Flip </Text>
</TouchableOpacity>
<TouchableOpacity
onPress={_takePhoto}
>
<Text>Snap Photo</Text>
</TouchableOpacity>
</View>
</Camera>
</View>
);
}
I did not check what the UI for that looks like but the main point is to utilize React.useRef and attach the ref to your <Camera/> component. Then you may call ref.current.takePictureAsync to capture and access a new image. Look below to see the important relevant snippets for capturing a photo.
import React from 'react'
/* ... other imports
*/
export default CameraScene = () => {
/* ... other state and permission logic
*/
const ref = useRef(null)
const _takePhoto = async () => {
const photo = await ref.current.takePictureAsync()
console.debug(photo)
}
return (
<Camera style={{flex: 1}} ref={ref}> /* ...
... other ui logic
*/
</Camera>
)
}
Learn more about useRef here https://reactjs.org/docs/hooks-reference.html#useref)
You'll need to add a ref to the Camera class to be able to call it's takePictureAsync function within your own 'handle' method.
cameraRef = React.createRef();
<Camera ref={this.cameraRef}>...</Camera>
Don't forget ".current" when calling the method of the referenced camera.
handlePhoto = async () => {
if(this.cameraRef){
let photo = await this.cameraRef.current.takePictureAsync();
console.log(photo);
}
}
Then simply call your 'handle' method on a touchable element acting as the photo-snap button.
<TouchableOpacity
style={{width:60, height:60, borderRadius:30, backgroundColor:"#fff"}}
onPress={this.handlePhoto} />
You should be able to see the photo logged in your console.
Are you trying to do this on an actual physical device? You can't shoot pictures with an emulator.

Implement #mention in TextInput

How can I implement #mention in react native's TextInput?
I've tried this react-native-mention but it is not being maintained anymore. There are so many styling issues and callback issues.
What I want is to display custom view inside TextInput. Something like this.
And after tapping on the list I want to display like this:
So far I am able to achieve:
When I type '#' in TextInput user list appear.
And when I tap on user I get username in TextInput
Code:
renderSuggestionsRow() {
return this.props.stackUsers.map((item, index) => {
return (
<TouchableOpacity key={`index-${index}`} onPress={() => this.onSuggestionTap(item.label)}>
<View style={styles.suggestionsRowContainer}>
<View style={styles.userIconBox}>
<Text style={styles.usernameInitials}>{!!item.label && item.label.substring(0, 2).toUpperCase()}</Text>
</View>
<View style={styles.userDetailsBox}>
<Text style={styles.displayNameText}>{item.label}</Text>
<Text style={styles.usernameText}>#{item.label}</Text>
</View>
</View>
</TouchableOpacity>
)
});
}
onSuggestionTap(username) {
this.setState({
comment: this.state.comment.slice(0, this.state.comment.indexOf('#')) + '#'+username,
active: false
});
}
handleChatText(value) {
if(value.includes('#')) {
if(value.match(/#/g).length > 0) {
this.setState({active: true});
}
} else {
this.setState({active: false});
}
this.setState({comment: value});
}
render() {
const {comments} = this.state;
return (
<View style={styles.container}>
{
this.state.active ?
<View style={{ marginLeft: 20}}>
{this.renderSuggestionsRow()}
</View> : null
}
<View style={{ height: 55}}/>
<View style={styles.inputContainer}>
<TextInput
style={styles.inputChat}
onChangeText={(value) => this.handleChatText(value)}
>
{comment}
</TextInput>
<TouchableOpacity style={styles.inputIcon} onPress={() => this.addComment()}>
<Icon type='FontAwesome' name='send-o' style={{fontSize: 16, color: '#FFF'}}/>
</TouchableOpacity>
</View>
</View>
);
}
One simple solution would be to use react-native-parsed-text.
Here is an example:
import * as React from "react";
import { Text, View, StyleSheet } from 'react-native';
import ParsedText from 'react-native-parsed-text';
const userNameRegEx = new RegExp(/#([\w\d.\-_]+)?/g);
export default class Example extends React.Component {
handleNamePress = (name) => {
alert("Pressed username " + name);
}
render() {
return (
<View style={styles.container}>
<ParsedText
style={styles.text}
parse={
[
{pattern: userNameRegEx, style: styles.username, onPress: this.handleNamePress},
]
}
childrenProps={{allowFontScaling: false}}
>
This is a text with #someone mentioned!
</ParsedText>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
text: {
color: 'black',
fontSize: 15,
},
username: {
color: 'white',
fontWeight: 'bold',
backgroundColor: "purple",
paddingHorizontal: 4,
paddingBottom: 2,
borderRadius: 4,
},
});
However, this library doesn't support rendering custom views. The example above is achieved by just pure styling. If you need a custom view you need to implement something yourself. For a long time, it wasn't possible to render arbitrary components embedded inside a text-components. However, this has changed now afaik and we can do stuff like this:
<Text>Hello I am an example <View style={{ height: 25, width: 25, backgroundColor: "blue"}}></View> with an arbitrary view!</Text>
Check both code examples here: https://snack.expo.io/#hannojg/restless-salsa
One important note: You can render the output of the ParsedText or your own custom component inside the TextInput, like this:
<TextInput
...
>
<ParsedText
...
>
{inputValue}
</ParsedText>
</TextInput>