React Native reusable edit component - react-native

I'm trying to create a reusable component in react native. The idea is to have only one component responsible to edit all the fields that I have.
Main Component
...
constructor(props) {
super(props);
this.state.FirstName = 'Joe'
}
...
const { FirstName } = this.state.FirstName;
<TouchableOpacity
onPress={() =>
NavigationService.navigate('EditData', {
label: 'First Name',
initialValue: FirstName,
onSubmit: (FirstName) => this.setState({ FirstName })
})
}
>
<CardItem>
<Left>
<FontAwesome5 name="user-edit" />
<Text>First Name</Text>
</Left>
<Right>
<Row>
<Text style={styles.valueText}>{FirstName} </Text>
<Icon name="arrow-forward" />
</Row>
</Right>
</CardItem>
</TouchableOpacity>
// Keep doing the same for other fields
Then, the edit component should be reusable.
constructor(props) {
super(props);
// callback function
this.onSubmit = props.navigation.getParam('onSubmit');
// label/value
this.state = {
label: props.navigation.getParam('label'),
value: props.navigation.getParam('initialValue')
};
}
render() {
const { onSubmit } = this;
const { label, value } = this.state;
return (
<Container>
<Header />
<Content>
<Item floatingLabel style={{ marginTop: 10 }}>
<Label>{label}</Label>
<Input
value={value}
onChangeText={val => this.setState({ value: val })}
/>
</Item>
<Button
onPress={() => {
onSubmit(value);
NavigationService.navigate('TenantDetails');
}
}
>
<Text>OK</Text>
</Button>
</Content>
</Container>
);
}
When back to the main component, the first name value was not changed.
My NavigationService in case it might be the problem:
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
};
Thanks

You could pass a callback to your new component which handles this. The new component would start with a state with the initialValue set. It looks like you might be using react-navigation so I would recommend that if you want this component on its own screen you could do
this.navigation.navigate('SetValueScreen', {
initialValue: this.state.email,
onSubmit: (email) => this.setState({ email })
})
and on the SetValueScreen get the initialValue in the constructor and in the render use the callback
class SetValueScreen extends React.PureComponent{
constructor(props){
super(props)
this.onSubmit = props.navigation.getParam('onSubmit');
this.state = {
value: props.navigation.getParam('initialValue')
}
}
render(){
const { onSubmit } = this
const { value } = this.state
return (
...
<Right>
<TextInput value={value} onChangeText={(value) => setState({ value })} />
</Right>
<Button onPress={() => {
onSubmit(value)
navigation.goBack()
}} >
OK
</Button>
...
)
}
}
I hope this helps.

Related

How do you use checkbox in map function in React Native?

My current code is like this.
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
checkedItems: new Map(),
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(id) {
this.setState((prevState) => ({ checkedItems: prevState.checkedItems.set(id, true) }));
console.log(this.state.checkedItems);
}
async componentDidMount() {
const items = ...;
this.setState({ items });
}
render() {
const { items } = this.state;
return (
<Container>
<Content>
<View>
{items.map((item) => (
<View key={item.id}>
<Text>{item.name}</Text>
<View>
<CheckBox
checked={this.state.checkedItems.get(item.id)}
onPress={() => this.handleChange(item.id)}
/>
</View>
</View>
))}
</View>
</Content>
</Container>
);
}
}
I would like to know how to uncheck.
Also, when I firstly check, console.log() in handleChange() outputs Map {}.
Is it correct?
Or if you know better way bay to use checkbox in map function, plz tell me.
I would appreciate it if you could give me advices.

Email address is badly formatted error without even typing anything

I am trying to do firebase authentication using email and password for my expo app. On the login screen, if i press on the login button, it gives me the above message, " email address is badly formatted'.
The signup button should navigate to sign up page, but clicking on the button also gives me the same error.
export default class App extends React.Component{
constructor(props)
{ super(props);
this.state = { isLoadingComplete: false,
};
if (!firebase.apps.length){firebase.initializeApp(ApiKeys.FirebaseConfig );}
}
render()
{
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
{Platform.OS === 'android' && <View style={styles.statusBarUnderlay} />}
{ <AppNavigator/> }
</View>
);
}
}
_loadResourcesAsync = async () => {
return Promise.all([
Asset.loadAsync([
require('./assets/images/robot-dev.png'),
require('./assets/images/robot-prod.png'),
]),
Font.loadAsync({
...Ionicons.font,
'space-mono': require('./assets/fonts/SpaceMono-Regular.ttf'),
}),
]);
};
_handleLoadingError = error => {
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
statusBarUnderlay: {
height: 24,
backgroundColor: 'rgba(0,0,0,0.2)',
},
});
## login page
export default class LoginScreen extends React.Component {
constructor(props)
{
super(props);
this.state = {
email:' ' ,password:' ',
}
}
LoginButtonPress= (email, password) =>
{
try{
firebase.auth().signInWithEmailAndPassword(this.state.email.trim(), this.state.password)
.then(function(user){console.log(user)
})
}
catch(error){
Alert.alert(error.message);
}
}
render()
{
return (
<ScrollView style= {styles.container}>
<Container style ={styles.container}>
<Content>
<Form>
<Item floatingLabel>
<Label> Email Address</Label>
<Input autoCorrect = {false} autoCapitalize ="none"
onChangeText= {(email) => this.setState({email})}/>
</Item>
<Item floatingLabel>
<Label> Password</Label>
<Input secureTextEntry ={true}
autoCorrect = {false}
autoCapitalize = "none"
onChangetext = { (password)=> this.setState({password})}/>
</Item>
<Button success info onPress ={()=>this.LoginButtonPress(this.state.email, this.state.password)}>
<Text> LogIn </Text>
</Button>
<Button primary onPress = {()=> this.props.navigation.navigate('SignupScreen')}>
<Text> First Time User? SignUpNow </Text>
</Button>
<Button onPress ={()=> this.props.navigation.navigate('ResetPassScreen')}>
<Text> Forgot Password? Reset Now </Text>
</Button>
</Form>
</Content>
</Container>
</ScrollView>
);
}}
##signup page
export default class SignupScreen extends React.Component{
constructor(props)
{
super(props);
this.state = { email: '',
password:''
}
}
SignUpButtonPress= (email, password)=>
{
try {
firebase.auth().createUserWithEmailAndPassword(this.state.email.trim(), this.state.password)
}
catch(error){
Alert.alert(error.message);
}
}
render ()
{
return (
<ScrollView>
<Container style = {styles.container}>
<Content>
<Form>
<Item floatingLabel>
<Label> Email Address</Label>
<Input autoCapitalize = "none" autoCorrect ={false} emailonChangeText ={(email)=>this.setState({email})} />
</Item>
<Item floatingLabel>
<Label> Password</Label>
<Input secureTextEntry = {true} onChangeText ={(password)=>this.setState({password})} />
</Item>
<Button success info onPress = {this.SignUpButtonPress(this.state.email, this.state.password)}>
<Text> SignUp </Text>
</Button>
</Form>
</Content>
</Container>
</ScrollView>
);
}
Your onPress for the signup button is immediately calling the SignupButtonPress callback on every render. Change
// notice that `onPress` is directly calling `this.SignUpButtonPress`
<Button success info onPress = {this.SignUpButtonPress(this.state.email, this.state.password)}>
To
<Button success info onPress = {() => {this.SignUpButtonPress(this.state.email, this.state.password);}}>

How to Pass DatepickerIOS Date Object to Firebase (React Native)

I'm EXTREMELY new at coding, and I took Grinder's course on React Native. I have no idea how to save the datepickerios date object into Firebase. I'm pretty sure something in this code here is incorrect.
Any help would be greatly appreciated. Also, if anyone's looking for a mentee, I'd be grateful for your help. Thanks!
class ScoreForm extends Component {
constructor(props) {
super(props);
this.state = {chosenDate: new Date()};
this.setDate = this.setDate.bind(this);
}
setDate(newDate) {
this.setState({chosenDate: newDate});
}
render() {
return (
<View>
<CardSection>
<Text style={styles.pickerTextStyle}>Enter Date</Text>
</CardSection>
<CardSection>
{/* <Input
label="Date"
placeholder="MM/DD/YY"
value={this.props.date}
onChangeText={value => this.props.scoreUpdate({ prop: 'date', value })}
/> */}
<DatePickerIOS
style={{ flex: 1 }}
mode='date'
date={this.state.chosenDate}
onDateChange={value => this.props.scoreUpdate({ prop: 'date', value })}
/>
</CardSection>
<CardSection>
<Input
label="Score"
placeholder="Add Number"
value={this.props.score}
onChangeText={value => this.props.scoreUpdate({ prop: 'score', value })}
/>
</CardSection>
</View>
);
}
}
const styles = {
pickerTextStyle: {
fontSize: 18,
paddingLeft: 20
}
};
const mapStateToProps = (state) => {
const { date, score } = state.scoreForm;
return { date, score };
};
export default connect(null, { scoreUpdate }) (ScoreForm);
Since I am using Redux, I'm assuming I that I don't want to store it in state, correct? Here is the this.props.ScoreUpdate action:
export const scoreUpdate = ({ prop, value }) => {
return {
type: SCORE_UPDATE,
payload: { prop, value }
};
};

How to call Screen / Component class method from react-navigation Header

I need to call SearchScreen class method from a React Navigation Header.
The Navigator look like this:
Search: {
screen: SearchScreen,
path: 'search/:query',
navigationOptions: {
title: 'Search',
header: {
right: (
<MaterialCommunityIcons
name="filter"
onPress={() => {
console.log(this);
}}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
},
}
}
I've made it work by doing:
// declare static navigationOptions in the Component
static navigationOptions = {
title: 'Title',
header: ({ state }) => ({
right: (
<MaterialCommunityIcons
name="filter"
onPress={state.params.handleFilterPress}
style={{marginRight: 15, color: 'white'}}
size={24}
/>
),
}),
}
_handleFilterPress() {
// do something
}
componentDidMount() {
// set handler method with setParams
this.props.navigation.setParams({
handleFilterPress: this._handleFilterPress.bind(this)
});
}
I've resolved the issue the following way:
static navigationOptions = ({ navigation }) => {
return {
headerRight: () => (
<TouchableOpacity
onPress={navigation.getParam('onPressSyncButton')}>
<Text>Sync</Text>
</TouchableOpacity>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ onPressSyncButton: this._onPressSyncButton });
}
_onPressSyncButton = () => {
console.log("function called");
}
Hooks solution with FunctionComponent, useState and useEffect
Ref the official docs (https://reactnavigation.org/docs/en/header-buttons.html#header-interaction-with-its-screen-component) it is done by:
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: <LogoTitle />,
headerRight: (
<Button
onPress={navigation.getParam('increaseCount')}
title="+1"
color="#fff"
/>
),
};
};
componentDidMount() {
this.props.navigation.setParams({ increaseCount: this._increaseCount });
}
state = {
count: 0,
};
_increaseCount = () => {
this.setState({ count: this.state.count + 1 });
};
/* later in the render function we display the count */
}
However I could not get this to work when working with the hooks api. My state variables were always undefined, but after I thought about how the hooks api is implemented it all made sense, so the solution was to update the navigation param every time a significant state variable was changed:
const [count, setCount] = useState(0);
useEffect(() => {
props.navigation.setParams({ increaseCount });
}, [count]);
const increaseCount = () => setCount(count + 1);
I came across same issue and able to resolve the issue from below links.
class MyScreen extends React.Component {
static navigationOptions = {
header: {
right: <Button title={"Save"} onPress={() => this.saveDetails()} />
}
};
saveDetails() {
alert('Save Details');
}
render() {
return (
<View />
);
}
}
Source: react-native issues 145
Below is my code
import React, { Component } from "react";
import {
Container,
Header,
Item,
Input,
Icon,
Button,
Text,
Left,
Body,
Right,
Content,
Spinner,
List,
ListItem
} from "native-base";
import { View, Image, StyleSheet, Keyboard } from "react-native";
import { connect } from "react-redux";
import {
onClear,
onSearchTextChanged,
searchForProfiles
} from "../../actions/searchActions";
class SearchBar extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Header searchBar rounded>
<Button
iconLeft
style={{ paddingLeft: 0 }}
light
onPress={this.props.onBackPress}
>
<Icon style={{ marginLeft: 0, fontSize: 35 }} name="arrow-back" />
</Button>
<Item>
<Icon name="ios-search" />
<Input
placeholder="Search"
onChangeText={this.props.onChangeText}
value={this.props.searchText}
/>
<Button small transparent onPress={this.props.onClear}>
<Icon name="ios-close" />
</Button>
</Item>
<Button transparent onPress={this.props.onSearch}>
<Text>Search</Text>
</Button>
</Header>
);
}
}
class SearchWorld extends Component {
static navigationOptions = ({ navigation }) => ({
left: null,
header: () => {
const { state } = navigation;
return (
<SearchBar
onBackPress={() => navigation.goBack()}
onChangeText={text => {
state.params.onChangeText(text);
}}
onSearch={state.params.onSearch}
onClear={state.params.onClear}
searchText={state.params.searchText}
/>
);
}
});
onChangeText = text => {
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: text
});
this.props.onSearchTextChanged(text);
};
onSearch = () => {
Keyboard.dismiss();
const profile = { search: "test" };
const token = this.props.token;
this.props.searchForProfiles(token, profile);
};
onClear = () => {
this.props.onClear();
this.props.navigation.setParams({
...this.props.navigation.state,
searchText: ""
});
};
componentDidMount() {
this.props.navigation.setParams({
onChangeText: this.onChangeText,
onSearch: this.onSearch,
onClear: this.onClear,
searchText: this.props.searchText
});
}
render() {
const { searchResults } = this.props;
let items = [];
if(searchResults && searchResults.data && searchResults.data.length > 0) {
items = [...searchResults.data];
}
return this.props.loading ? (
<Container style={{ alignItems: "center", justifyContent: "center" }}>
<Spinner color="#FE6320" />
</Container>
) : (
<Container>
<Content>
<List
style={{}}
dataArray={items}
renderRow={item => (
<ListItem style={{ marginLeft: 0}}>
<Text style={{paddingLeft: 10}}>{item.password}</Text>
</ListItem>
)}
/>
</Content>
</Container>
);
}
}
const mapStateToProps = state => {
const { token } = state.user;
const { searchText, searchResults, error, loading } = state.searchReaducer;
return {
token,
searchText,
searchResults,
error,
loading
};
};
export default connect(mapStateToProps, {
onClear,
onSearchTextChanged,
searchForProfiles
})(SearchWorld);
static navigationOptions = ({navigation}) => {
return {
headerTitle: () => <HeaderTitle />,
headerRight: () => (<Button iconLeft transparent small onPress = {navigation.getParam('onPressSyncButton')}>
<Icon style ={{color:'white', fontWeight:'bold'}} name='md-save' size = {32} />
<Text style ={{color:'white', fontWeight:'bold'}}>save</Text>
</Button>),
headerTintColor:'black',
headerStyle: {
backgroundColor: '#6200EE'
},
}
};
this.props.navigation.setParams({ onPressSyncButton: this.updateUserProfile });

Passing props between views in React Native

I am trying to simply pass props between views in my React-Native app. Namely, I am trying to collect data from a text input, and pass it to the next view in a string. This is what I have:
first view:
constructor(props) {
super(props);
this.state = {name: "", email: "" };
}
_pressRow(){
let name=this.state.name;
let email=this.state.email;
this.props.navigator.push({
ident: "ImportContactScreen",
sceneConfig: Navigator.SceneConfigs.FloatFromRight,
passProps: { name: name}
});
}
<TextInput
style={styles.input}
ref= "name"
onChangeText={(name) => this.setState({name})}
value={this.state.name}
/>
<F8Button
style={styles.button}
onPress={() => this._pressRow()}
caption="Continue"
/>
second view:
props = {name}
<Text style={styles.h1}>Hi, {this.props.name}. Let`'`s import your contacts:</Text>
The navigator I'm using looks like:
class MyAppNavigator extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<Navigator
ref="appNavigator"
initialRoute={this.props.initialRoute}
renderScene={this._renderScene}
configureScene={(route) => ({
...route.sceneConfig || Navigator.SceneConfigs.FloatFromRight
})}
/>
);
}
_renderScene(route, navigator) {
var globalNavigatorProps = { navigator }
switch (route.ident) {
case "LoginScreen":
return <LoginScreen {...globalNavigatorProps} />
case "UserFlowScreen":
return <UserFlowScreen {...globalNavigatorProps} />
case "ImportContactScreen":
return <ImportContactScreen {...globalNavigatorProps} />
default:
return <LoginScreen {...globalNavigatorProps} />
}
}
};
module.exports = MyAppNavigator;
When I run this, this.props.name comes up blank
passProps is a property of route. Try adding {...route.passProps} to each return in your switch statement:
return <LoginScreen {...globalNavigatorProps} {...route.passProps} />