creating a TextInput component that can output normal and output a ****/ secureTextEntry={true} - react-native

So I have a problem creating a text input component just like when IGNITE CLI created a component named RoundedButton (code shown below). I want to create a similar component but a TextInput that can output a normal output and output a **** character whenever pressed and needed with simple tweaking. How can I do this?
This is the code for RoundedButton :
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { TouchableOpacity, Text } from 'react-native'
import styles from './Styles/RoundedButtonStyles'
import ExamplesRegistry from '../Services/ExamplesRegistry'
// Note that this file (App/Components/RoundedButton) needs to be
// imported in your app somewhere, otherwise your component won't be
// compiled and added to the examples dev screen.
// Ignore in coverage report
/* istanbul ignore next */
ExamplesRegistry.addComponentExample('Rounded Button', () =>
<RoundedButton
text='real buttons have curves'
onPress={() => window.alert('Rounded Button Pressed!')}
/>
)
export default class RoundedButton extends Component {
static propTypes = {
onPress: PropTypes.func,
text: PropTypes.string,
children: PropTypes.string,
navigator: PropTypes.object
}
getText () {
const buttonText = this.props.text || this.props.children || ''
return buttonText.toUpperCase()
}
render () {
return (
<TouchableOpacity style={styles.button} onPress={this.props.onPress}>
<Text style={styles.buttonText}>{this.getText()}</Text>
</TouchableOpacity>
)
}
}

Have the secureTextEntry linked to component state/prop and change it on button press.
So like this:
secureTextEntry={this.state.showDots}
and then onPress of button
onPress = () => {
this.setState({ showDots: true/false})
}

Related

NativeBase: Button does not work, but ReactNative's Button does

Experiencing a strange issue in my React Native project for Android.
Using React-Navigation, I have a component with a button inside. This button should navigate to a new screen.
Thing is, the built-in button of React Native works like a charm, while the button of Native Base does not. I am completely confused, even more because I use this Native Base Button in another location, too. And there it works fine.
What is going on here?
Here, you see the application works with the built-in React Native button:
On the opposite, using the button of Native Base, it not only does not work, even styles are not applied.
Here is the code with the React Native button:
import React from "react";
import { Button, View, Text, StyleSheet } from "react-native";
import { withNavigation } from "react-navigation";
type Props = { navigation: any };
const ButtonTestScreen: React.FC<Props> = ({ navigation }) => {
return (
<View>
<Button
title="Hi i am a button"
onPress={() => navigation.navigate("Details")}
></Button>
</View>
);
};
export default withNavigation(ButtonTestScreen);
And the code with Native Base button:
import React from "react";
import { Button, View, Text, StyleSheet } from "react-native";
import { withNavigation } from "react-navigation";
import ButtonNavigate from "../../components/atoms/ButtonNavigate/ButtonNavigate";
type Props = { navigation: any };
const ButtonTestScreen: React.FC<Props> = ({ navigation }) => {
return (
<View>
<ButtonNavigate
title="Hi i am a button"
navigateTo="Details"
></ButtonNavigate>
</View>
);
};
const styles = StyleSheet.create({
button_style: {
backgroundColor: "red"
},
text_style: {
color: "#000",
fontSize: 30
}
});
export default withNavigation(ButtonTestScreen);
And the respective ButtonNavigate component itself:
import React from "react";
import { StyleSheet } from "react-native";
import { withNavigation } from "react-navigation";
import { Button, Text } from "native-base";
type Props = {
title: string,
navigateTo: string,
navigation: any
};
const ButtonNavigate: React.FC<Props> = ({ title, navigateTo, navigation }) => {
return (
<Button
rounded
transparent
style={styles.button_style}
onPress={() => navigation.navigate(navigateTo)}
>
<Text style={styles.text_style}>{title}</Text>
</Button>
);
};
const styles = StyleSheet.create({
button_style: {
backgroundColor: "red"
},
text_style: {
color: "#151414"
}
});
export default withNavigation(ButtonNavigate);
I have just tested you code in expo.snack but without navigation and its ok,
see it here
You can test in your app to remove navigation and go step by step until you find the bug.
Folks, reason for this strange behavior is the "rounded" property of Native Base's button. In my application, somehow it causes the button to become non-clickable.
Maybe contributors of Native Base know what to do with this problem, so if you read this, maybe you have an idea.
Solution for my now was simply removing "rounded".
Native Base: 2.13.8
React-Navigation: 4.0.10
In my case it was the "top" in the container property of the button causing this issue. Removed it and adding "marginBottom" to the container above it solved the issue

React call function and setstate when go back to a previous screen

I'm new in React's world
I have 2 screens : Stock and Barcode.
In Stock, i navigate to Barcode's screen.
When i scan a barcode, i go back to the previous screen I would like to set the input text with the barcode and call a function. In my example joinData();
The problem is to set the input text and call a function.
I tried examples and answers but i don't find or don't understand how to to that.
I tried something in componentDidUpdate() but it fails
Invariant Violation:Maximum update depth exceeded
Stock.js
import React, {useState} from "react";
import { ScrollView, TouchableWithoutFeedback, Dimensions, StyleSheet, FlatList, View, Alert, TouchableOpacity, TextInput } from 'react-native';
//galio
import { Block, Text, theme } from "galio-framework";
import { Button, Icon, Input } from "../components/";
export default class Stock extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.array = [];
this.state = {
arrayHolder: [],
Input_ITMREF: ''
};
}
// I tried this but it fails
componentDidUpdate() {
if (this.props.navigation.getParam('itmref') != 'undefined') {
this.setState({ Input_ITMREF: this.props.navigation.getParam('itmref')});
}
}
componentDidMount() {
this.setState({ arrayHolder: [...this.array] }) // Rafraîchit la liste
}
joinData = () => {
vxml = this.state.Input_ITMREF+" I do something";
}
Render() {
return (
<Block flex>
<Block row space="evenly">
<Block center>
<Input
placeholder='Code article'
onChangeText={data => this.setState({ Input_ITMREF: data })}
ref={this.myRef}
/>
</Block>
</Block>
<Block center>
<Button style={styles.button} onPress={() => this.props.navigation.navigate('Barcode')}>Barcode</Button>
<Text style={{ margin: 10 }}>Post: {this.props.navigation.getParam('itmref')}</Text>
</Block>
</Block>
);
}
}
And Barcode.js
import React, {} from 'react';
import { ScrollView, TouchableWithoutFeedback, Dimensions, StyleSheet, FlatList, View, Alert, TouchableOpacity, TextInput } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import { Button } from "../components/";
export default class Barcode extends React.Component {
static navigationOptions = {
header: null //hide the header bar
};
handleBarCodeScanned = ({ type, data }) => {
this.props.navigation.navigate("Stock", {
itmref: data
});
};
render() {
return (
<BarCodeScanner
onBarCodeScanned={this.handleBarCodeScanned}
style={styles.barcodeScanner}
/>
);
}
}
You can pass a state handler function as prop to Barcode screen and use that to set value for textInput in state.
in Stock(in state)
state = {
inputValue: ''
}
....
const setInputTextValue= (newValue) => {
this.setState({
inputValue: newValue
})
you pass this function as prop to Barcode scene and call it whenever you wanna set a new value(considering Stock scene is still mounted).
UPDATE: What is the proper way to update a previous StackNavigator screen?
also another solution i just saw: Updating State of Another Screen in React Navigation
You need to use WillFocus method(Included in react-navigation) when you comeback from Barcodepage to stockPage
componentDidMount(){
console.log("willFocus runs") initial start
const {navigation} = this.props;
navigation.addListener ('willFocus', async () =>{
console.log("willFocus runs") // calling it here to make sure it is logged at every time screen is focused after initial start
});
}
For More Information read this document
https://reactnavigation.org/docs/function-after-focusing-screen/

React Navigation - How to pass data across different screens in TabNavigator?

I have a TabNavigator, and in each tab is a StackNavigator. Inside the StackNavigator, I have screens. The screens in each Tab do not call each other directly; the TabNavigator handles the screen changes when a tab is pressed.
In the first tab, if the user clicks a button, some data is created. If the user then navigates to the second Tab, I would like to pass this data to the screen in the second Tab.
Here is a demo of the code:
import React from 'react';
import { Button, Text, View } from 'react-native';
import {
createBottomTabNavigator,
createStackNavigator,
} from 'react-navigation';
class HomeScreen extends React.Component {
doIt = () => {
this.props.navigation.setParams({results: ['one', 'two']}); // <--- set data when user clicks button.
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/* other code from before here */}
<Button
title="Set Results"
onPress={this.doIt}
/>
</View>
);
}
}
class SettingsScreen extends React.Component {
render() {
console.log(this.props.navigation); // <--- console out when user clicks on this tab
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings</Text>
</View>
);
}
}
const HomeStack = createStackNavigator({
Home: HomeScreen,
});
const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
});
export default createBottomTabNavigator(
{
Home: HomeStack,
Settings: SettingsStack,
},
{
}
);
The this.props.navigation.state.params never gets the data results in the second Tab. There isn't even a key for it, so if I try to access this.props.navigation.state.params.results, it will be undefined.
This is confusing because I thought props.navigation is passed to all screens automatically.
How can I pass data from one screen to another through the TabNavigator, using just react-navigation? I have seen answers that say to use Redux, but I would not like to import another library if all I want is to keep some state across screens in different react navigators.
It may seem that this.props.navigation.state.params is only able to old one parameter? Possibly? Try this:
doIt = () => {
this.props.navigation.setParams({results: 'one'}); // <--- set data when user clicks button.
}
console.log(this.props.navigation.state.params.results);
Setting props did not work when passing data across different tabs. I even tried playing with AsyncStorage, trying to save and retrieve them in different tabs.
I ended up using Redux to save my states, and that has worked well so far.
I came across a similar problem. I had a multi page form that the client insisted on having each step be enclosed in a tab on a tab bar. I used the react navigation createMaterialTopTabNavigator to create the navigator and couldn't find an easy way to pass the form data between tabs.
What I end up doing was using react's Context API and wrapped the tab navigator in a root form container that provides the context value to the navigator and routes inside. Here is how I did it:
Root form container
// MultiScreenForm.js
imports...
import MultiScreenFormNavigator from './MultiScreenFormNavigator'
export const FormContext = React.createContext()
class MultiScreenForm extends Component {
constructor(props) {
super(props)
this.state = {
// formDataHere
formUpdaters: {
onToggleOptIn: this.handleToggleOptIn // example updater method
// other
}
}
}
handleToggleOptIn = () => {
// toggle opt in form data with this.setState
}
render() {
return (
<FormContext.Provider value={this.state}>
<MultiScreenFormNavigator />
</FormContext.Provider>
)
}
}
export default MultiScreenForm
Example form page
// ProfileForm.js
imports...
import { FormContext } from './MultiScreenForm'
class ProfileForm extends Component {
render() {
// FormContext.Consumer uses function as child pattern
return (
<FormContext.Consumer>
{ (context) => (
// our form can now use anything that we pass through the context
// earlier, we passed the root form's state including an updater
<button onPress={context.formUpdaters.onToggleOptIn} />
// ...
)
}
</FormContext.Consumer>
)
}
}
export default ProfileForm
Tab navigator
// MultiScreenFormNavigator.js
imports...
import ProfileForm from './ProfileForm'
import { createMaterialTopTabNavigator } from 'react-navigation'
const MultiScreenFormNavigator = createMaterialTopTabNavigator(
{
Profile: ProfileForm,
// AnotherForm: AnotherForm
},
// { navigator options here... }
)
export default MultiScreenFormNavigator
We then render the MultiScreenForm instead of the tab navigator directly.
This worked for me but I feel there should be an easier way to do this. I hope people who read this can share their approaches.
#tempomax
tried same with AsyncStorage but data came in with a delay.
Sometimes you don't need Redux if your app stays small.
So tried to find a way without Redux.
Here is what I came up with
I hope it's not too late to answer.
Solved it with NavigationEvents and setting params to Route.
The problem with tab is that you can´t pass params to screen because navigation.navigate will be triggered automatically if createMaterialTopTabNavigator is swiped or clicked on non-active TabBar Button.
This can be solved with NavigationEvent like follow.
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus',payload)}
onDidFocus={payload => console.log('did focus',payload)}
onWillBlur={payload =>
/*
if screen is about to change this will be triggred
In screen 'MyScreen2' you can get it with navigation.params
*/
this.props.navigation.navigate('MyScreen2', { name: 'Brent' })
}
onDidBlur={payload => console.log('did blur',payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;
Now you can get the data in MyScreen2
/* 2. Get the param, provide a fallback value if not available */
const { navigation } = this.props;
const itemId = navigation.getParam('name', 'DefaultName');
const otherParam = navigation.getParam('otherParam', 'some default value');
If you are using React Native Navigation Version 5.x with a DrawerNavigation, you can do this using
in screen 1:
<Button
onPress={() => {
this.props.navigation.navigate(<ScreenNameOfDrawerScreen>,
{screen:'<ScreenNameInTabDrawer>',params:{your_json_Data}});
}} />
in screen 2:
............
render() {
if(this.props.route.params!=undefined){
if(this.props.route.params.your_json_Data!=null){
// Use this.props.route.params.your_json_Data. It is your json data.
}
}
return (
..............

React-Native, ActionSheetIOS display instead of Picker

I would like to use the ActionSheetIOS on iOS, instead of the native picker wheel.
My app crashes out, how can I display my component?
Here is my Picker component:
// Picker.ios.js
import React, { Component } from "react";
import { StyleSheet, ActionSheetIOS, View } from "react-native";
const PickerList = props => {
const { label, options, selectedValue, name, onChange, identifier } = props;
return ActionSheetIOS.showActionSheetWithOptions(
{
options: options,
cancelButtonIndex: 1,
destructiveButtonIndex: 2
},
optionIndex => {
console.log('clicked')
}
);
};
export default PickerList;
I'm using conditional rendering to display my pickers, and a platform specific import:
import Picker from "./common/Picker";
{setYear
? <Picker
selectedValue={setGroup}
label="Year group"
onChange={this.onGroupChange}
options={
categories.find(category => {
return category.id == setYear;
}).options
}
/>
: null}
ActionSheetIOS only shows the options, you have to have some view component to replace the picker in the view. I used TouchableOpacity ja ActionSheetIOS to replace Picker on IOS like this:
<TouchableOpacity onPress={this.onSelectCategory.bind(this)}>
<Text style={styles.textInputStyle}>
{this.state.category}
</Text>
</TouchableOpacity>
onSelectCategory() {
ActionSheetIOS.showActionSheetWithOptions(
{ options: FEEDBACK_CATEGORIES},
(buttonIndex) => this.setState({ category: FEEDBACK_CATEGORIES[buttonIndex] }));
}
I use this.state.category to show my selection in TouchableOpacity
When user press the TouchableOpacity the ActionSheetIOS is shown
When user select option the callback function is called and I update the
selected index to the this.state.category

React Native - OnPress button not working

I have a button with a onPress event i am trying to simply console log that the button was successfully pressed.
The button is within my template file i have extracted this portion of code to a separate file, ive done the same for my tyles.
The add() function associated with the press does not fire when i press the add button, so the console.log does not show.
But the console.log does does show on initial load of the screen ( i dont know why this is )
items.screen.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, TextInput } from 'react-native';
import style from './items.style'
import template from './items.template';
export default class ItemsScreen extends Component {
static navigationOptions = {
title: "Items"
}
constructor(props) {
super(props);
this.state = { text: '' };
}
add() {
console.log("pressed add button");
}
render() {
return template(this, style);
}
}
items.template.js
import React, { Component } from 'react';
import { Text, View, TextInput, Button } from 'react-native';
import { style } from './items.style'
export default (template, style) => {
return (
<View style={style.container}>
<TextInput
style={{ width: 300 }}
value={template.state.text}
onChangeText={(text) => template.setState({ text })}
/>
<Button
onPress={template.add()}
title="Add"
/>
</View>
);
}
You are not assigning template.add to the on press event, instead your code is executing the add function and attempting to assign the result of that to the onPress event.
I find the following two options to be cleaner than the other suggestions:
Create a local onPress Handler (in items.template)
onPress = () => {
template.add();
}
and then
onPress={this.onPress} //note the lack of parentheses `()`
or
create an inline function
onPress={() => template.add()}
You should pass function in attribute,not function call, so it should be
<Button
onPress={template.add.bind(template)}
title="Add"
/>