I have a screen where the user inputs a phone number. I run a graphql query loadUsers according to the input and then display the search results via the showUsersfunction. It works fine on the first time. I get the results. However, after that, when the results are conditionally rendered, the search button becomes disabled. So if I want to type in a different phone number and hit the search button again, I can't do this. Unless I exit the screen and then come back. How can I fix this?
Here's what my code looks like:
export const AddContactTry: React.FunctionComponent = () => {
const initialValues: FormValues = {
phoneNumber: '',
};
const [isSubmitted, setIsSubmitted] = useState(false);
const [userData, setUserData] = useState<UsersLazyQueryHookResult>('');
const navigation = useNavigation();
const validationSchema = phoneNumberValidationSchema;
const [
createUserRelationMutation,
{
data: addingContactData,
loading: addingContactLoading,
error: addingContactError,
called: isMutationCalled,
},
] = useCreateUserRelationMutation({
onCompleted: () => {
Alert.alert('Contact Added');
},
});
const showUsers = React.useCallback(
(data: UsersLazyQueryHookResult) => {
if (data) {
return (
<View style={styles.users}>
{data.users.nodes.map(
(item: { firstName: string; lastName: string; id: number }) => {
const userName = item.firstName
.concat(' ')
.concat(item.lastName);
return (
<View style={styles.item} key={item.id}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/afro_woman_female_person-512.png',
}}></Thumbnail>
<Text style={styles.userName}>{userName}</Text>
<View style={styles.addButtonContainer}>
<Button
rounded
style={styles.addButton}
onPress={() => {
addContact(Number(item.id));
setIsSubmitted(false);
setUserData(null);
}}>
<Icon
name="plus"
size={moderateScale(20)}
color="black"
/>
</Button>
</View>
</View>
);
},
)}
</View>
);
}
},
[createUserRelationMutation, userData],
);
const addContact = React.useCallback(
(id: Number) => {
console.log('Whats the Id', id);
createUserRelationMutation({
variables: {
input: { relatedUserId: id, type: RelationType.Contact, userId: 30 },
},
});
},
[createUserRelationMutation],
);
const getContactId = React.useCallback(
(data: UsersLazyQueryHookResult) => {
if (data) {
if (data.users.nodes.length == 0) {
Alert.alert('No User Found');
} else {
setUserData(data);
}
}
},
[addContact],
);
const [loadUsers] = useUsersLazyQuery({
onCompleted: getContactId,
onError: _onLoadUserError,
});
const handleSubmitForm = React.useCallback(
(values: FormValues, helpers: FormikHelpers<FormValues>) => {
setIsSubmitted(true);
const plusSign = '+';
const newPhoneNumber = plusSign.concat(values.phoneNumber);
loadUsers({
variables: {
where: { phoneNumber: newPhoneNumber },
},
});
values.phoneNumber = '';
},
[loadUsers],
);
return (
<SafeAreaView>
<View style={styles.container}>
<View style={styles.searchTopContainer}>
<View style={styles.searchTopTextContainer}>
</View>
<View>
<Formik
initialValues={initialValues}
onSubmit={handleSubmitForm}
validationSchema={validationSchema}
>
{({ handleChange, handleBlur, handleSubmit, values, isValid, dirty }) => (
<View style={styles.searchFieldContainer}>
<View style={styles.form}>
<FieldInput style={styles.fieldInput}
handleChange={handleChange}
handleBlur={handleBlur}
value={values.phoneNumber}
fieldType="phoneNumber"
icon="phone"
placeholderText="49152901820"
/>
<ErrorMessage
name="phoneNumber"
render={(msg) => (
<Text style={styles.errorText}>{msg}</Text>
)}
/>
</View>
<View style={styles.buttonContainer}>
<Text>Abbrechen</Text>
</Button>
<Button
block
success
disabled={!isValid || !dirty}
onPress={handleSubmit}
style={styles.button}>
<Text>Speichern</Text>
</Button>
</View>
</View>
)}
</Formik>
</View>
{isSubmitted && showUsers(userData)}
</View>
</View>
</SafeAreaView>
);
};
Edit:
As suggested in comments, I tried using useFormik instead of and moved showUsers to a separate component but it didn't work either. The button still gets disabled after first query.
export const AddContactTry: React.FunctionComponent = () => {
const validationSchema = phoneNumberValidationSchema;
const { values, handleChange, handleSubmit, dirty, handleBlur, isValid, resetForm, isSubmitting, setSubmitting, touched}= useFormik({
initialValues: {
phoneNumber: '',
},
//isInitialValid:false,
validationSchema,
onSubmit: (values: FormValues) => {
handleSubmitForm(values);
},
});
console.log('isDirty', dirty);
console.log('isValid', isValid);
console.log('phone numm', values.phoneNumber);
console.log('submitting status', isSubmitting);
const [isSubmitted, setIsSubmitted] = useState(false);
const [userData, setUserData] = useState<UsersLazyQueryHookResult>('');
const navigation = useNavigation();
const _onLoadUserError = React.useCallback((error: ApolloError) => {
Alert.alert('Oops, try again later');
}, []);
// const [
// createUserRelationMutation,
// {
// data: addingContactData,
// loading: addingContactLoading,
// error: addingContactError,
// called: isMutationCalled,
// },
// ] = useCreateUserRelationMutation({
// onCompleted: () => {
// Alert.alert('Contact Added');
// },
// });
// const showUsers = React.useCallback(
// (data: UsersLazyQueryHookResult) => {
// if (data) {
// return (
// <View style={styles.users}>
// {data.users.nodes.map(
// (item: { firstName: string; lastName: string; id: number }) => {
// const userName = item.firstName
// .concat(' ')
// .concat(item.lastName);
// return (
// <View style={styles.item} key={item.id}>
// <Thumbnail
// style={styles.thumbnail}
// source={{
// uri:
// 'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/afro_woman_female_person-512.png',
// }}></Thumbnail>
// <Text style={styles.userName}>{userName}</Text>
// <View style={styles.addButtonContainer}>
// <Button
// rounded
// style={styles.addButton}
// onPress={() => {
// //addContact(Number(item.id));
// setIsSubmitted(false);
// setUserData(null);
// }}>
// <Icon
// name="plus"
// size={moderateScale(20)}
// color="black"
// />
// </Button>
// </View>
// </View>
// );
// },
// )}
// </View>
// );
// }
// },
// [createUserRelationMutation, userData],
// );
// const addContact = React.useCallback(
// (id: Number) => {
// console.log('Whats the Id', id);
// createUserRelationMutation({
// variables: {
// input: { relatedUserId: id, type: RelationType.Contact, userId: 30 },
// },
// });
// },
// [createUserRelationMutation],
// );
const getContactId = React.useCallback(
(data: UsersLazyQueryHookResult) => {
//resetForm();
if (data) {
if (data.users.nodes.length == 0) {
Alert.alert('No User Found');
} else {
setUserData(data);
}
}
},
//[addContact],
[],
);
const [loadUsers] = useUsersLazyQuery({
onCompleted: getContactId,
onError: _onLoadUserError,
});
const handleSubmitForm = React.useCallback(
(values: FormValues) => {
setIsSubmitted(true);
const plusSign = '+';
const newPhoneNumber = plusSign.concat(values.phoneNumber);
console.log('Submitted');
loadUsers({
variables: {
where: { phoneNumber: newPhoneNumber },
},
});
resetForm();
},
[loadUsers],
);
// if (!addingContactLoading && isMutationCalled) {
// if (addingContactError) {
// Alert.alert('Unable to Add Contact');
// }
// }
return (
<SafeAreaView>
<View style={styles.container}>
<View style={styles.searchTopContainer}>
<View>
<View style={styles.searchFieldContainer}>
<View style={styles.form}>
<Item underline style={styles.newFieldInput} >
<Icon name="mobile" color="black" size={26}></Icon>
<Input
onChangeText={handleChange('phoneNumber') as (text: string) => void}
onBlur={handleBlur('phoneNumber') as (event: any) => void}
value={values.phoneNumber}
placeholder="49152901820"
/>
</Item>
</View>
<View style={styles.buttonContainer}>
<Button
block
danger
bordered
style={styles.button}
// onPress={() => navigation.goBack()}
//disabled={!isValid || !dirty}
//disabled={isSubmitting}
onPress={resetForm}
>
<Text>Abbrechen</Text>
</Button>
<Button
block
success
disabled={!isValid || !dirty}
onPress={handleSubmit}
style={styles.button}>
<Text>Speichern</Text>
</Button>
</View>
</View>
</View>
{/* {isSubmitted && showUsers(userData)} */}
<User data={userData}></User>
</View>
</View>
</SafeAreaView>
);
};
type UserProps = {
data: UsersLazyQueryHookResult;
//isSubmitted: boolean;
};
export const User: React.FunctionComponent<UserProps> = ({
data,
//isSubmitted,
}) => {
console.log('user called');
const [
createUserRelationMutation,
{
data: addingContactData,
loading: addingContactLoading,
error: addingContactError,
called: isMutationCalled,
},
] = useCreateUserRelationMutation({
onCompleted: () => {
Alert.alert('Contact Added');
},
});
const addContact = React.useCallback(
(id: Number) => {
console.log('Whats the Id', id);
createUserRelationMutation({
variables: {
input: { relatedUserId: id, type: RelationType.Contact, userId: 30 },
},
});
},
[createUserRelationMutation],
);
if (!addingContactLoading && isMutationCalled) {
if (addingContactError) {
Alert.alert('Unable to Add Contact');
}
}
if (!data) return null;
return (
<View style={styles.users}>
{data.users.nodes.map(
(item: { firstName: string; lastName: string; id: number }) => {
const userName = item.firstName.concat(' ').concat(item.lastName);
return (
<View style={styles.item} key={item.id}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/afro_woman_female_person-512.png',
}}></Thumbnail>
<Text style={styles.userName}>{userName}</Text>
<View style={styles.addButtonContainer}>
<Button
rounded
style={styles.addButton}
onPress={() => {
addContact(Number(item.id));
//setIsSubmitted(false);
//setUserData(null);
}}>
<Icon name="plus" size={moderateScale(20)} color="black" />
</Button>
</View>
</View>
);
},
)}
</View>
);
};
The button is supposed be disabled (grey) when it's empty (not dirty) and not valid (```!isValid). If it's dirty and valid, the button turns to green. Currently, after running the first query and getting the results, If I type something valid into the input field, the button does turn to green from grey. However, I cannot 'click' on it.
Make few changes to your code and see if it works:
make <Button> type submit.
Make sure to provide a name (phoneNumber) to your input. This is how formik tracks the form values.
<FieldInput style={styles.fieldInput}
handleChange={handleChange}
handleBlur={handleBlur}
value={values.phoneNumber}
fieldType="phoneNumber"
name="phoneNumber" //<<<<<<<--- like this
icon="phone"
placeholderText="49152901820"
/>
use <form> tag inside <Formik>. Have an onSubmit.
for example:
<Formik
initialValues={{ name: 'jared' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
>
{({ handleChange, handleBlur, handleSubmit, values, isValid, dirty }) => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
</Formik>
don't mutate values. Use resetForm instead
const handleSubmitForm = React.useCallback(
(values: FormValues, formikBag: any) => {
setIsSubmitted(true);
const plusSign = '+';
const newPhoneNumber = plusSign.concat(values.phoneNumber);
console.log('Submitted');
loadUsers({
variables: {
where: { phoneNumber: newPhoneNumber },
},
});
// values.phoneNumber = ''; //<------don't do this.. probably this could be issue as well
formikBag.resetForm()
},
[loadUsers],
);
remove all React.useCallbacks. Once your form is working then add it one by one to required methods
Following comment's disscussion (enter link description here) it looks like React Native fails in some specific scenarios. Updated state/variables are not properly reflected to rendered view (button rerendered as not disabled doesn't work).
It's not Formik's fault ... using useFormik gives possibility to access values and helpers in entire component. resetForm called from handlers works properly.
My advise is to extract showUsers into separate [functional] component, f.e.
{userData && <UserList data={userData} />}
or at least use key in rendered <View /> components at the levels where there is more than one (showUsers rendered <View/> at a sibbling to <View style={styles.searchTopContainer}> ). Using key helps react to manage virtual DOM and update view. Separate component in fact does the same but also reduces this component complexity.
Related
I can see there's a lot of similar questions, but none of the answer could help me.
I get this error when running the code below: TypeError: undefined is not an object (evaluating 'this.state')
I also get this error, but this is connected to this.setState(): TypeError: _this.setState is not a function. (In '_this.setState({
checked: !_this.state.checked
})', '_this.setState' is undefined)
<CheckBox
center
title='Click Here'
checked={this.state.checked}
onPress={() => this.setState({checked: !this.state.checked})}
/>
The whole code (if necessary):
export default function NewEventScreen() {
const [date, setDate] = useState(new Date());
const [mode, setMode] = useState('date');
const [show, setShow] = useState(false);
const onChange = (event, selectedDate) => {
const currentDate = selectedDate || date;
setShow(Platform.OS === 'ios');
setDate(currentDate);
};
const showMode = (currentMode) => {
setShow(true);
setMode(currentMode);
};
const showDatepicker = () => {
showMode('date');
};
const showTimepicker = () => {
showMode('time');
};
handleEventCreation = () => {
const { title, description } = this.state
firebase.firestore()
.collection('users').doc(firebase.auth().currentUser.uid).collection('events').doc().set({
title: title,
description: description,
}).then(() => { console.log("Document written")
this.setState({
title: '',
description: '',
})
}).catch(error => console.log(error))
}
state = {
title: '',
description: '',
checked: false,
}
onPress = () => {
this.setState({checked: !this.state.checked})
}
return (
<View style={styles.container}>
<Text style={styles.title}>Here can you make your events!</Text>
<TextInput
style={styles.inputBox}
value={state.title}
onChangeText={title => this.setState({ title })}
placeholder='Title'
/>
<TextInput
style={styles.inputBox}
value={state.description}
onChangeText={description => this.setState({ description })}
placeholder='Description'
/>
<CheckBox
center
title='Click Here'
checked={this.state.checked}
onPress={() => this.onPress()}
/>
<View>
<View>
<Button onPress={showDatepicker} title="Show date picker!" />
</View>
<View>
<Button onPress={showTimepicker} title="Show time picker!" />
</View>
{show && (
<DateTimePicker
testID="dateTimePicker"
value={date}
mode={mode}
is24Hour={true}
display="default"
onChange={onChange}
/>
)}
</View>
<TouchableOpacity style={styles.button} onPress={handleEventCreation}>
<Text style={styles.buttonText}>Create Event</Text>
</TouchableOpacity>
</View>
)
}
You are using this.setState inside a functional component. You cannot mix those two together (you can only use it inside a class component). If you want to use checked you need to add const [checked, setChecked] = useState(false); and use setChecked to set state of checked. Same goes for title and description.
I am new to coding. Currently tryin to make a mobile app in react-native + firebase.
I am stuck at some really simple ( as i think ) stage, but spent a couple of days now but cannot find an answer.
In my app, user can create or enter room. Once created, room generates specific folder in firebase.
Creating and entering rooms work fine.
But whhen user enters a new room , he observes the state from the previous room even though it suppose to be blank as this is his first entry.
I dont understand how to solve this problem, absolutely crying now. A
Any help is appreciated.
This is a part of code where state is saved:
class ChatRoom extends React.Component {
constructor(zaza) {
super(zaza);
this.state = {
allTasksComplete: false,
task1: false,
task2: false,
task3: false,
task4: false,
task5: false,
task6: false,
task7: false,
finishedTasks: null,
zozo: false,
newRoomName: '',
creator: '',
};
}
generateTask = async (taskIndex, taskName,) => {
const creatorCheck = await AsyncStorage.getItem('nickname')
this.setState({creator: creatorCheck})
const nicknameSnap = await collectionRef.where('roomName', '==', this.state.newRoomName).get()
const nickCheck = nicknameSnap.docs[0].data()
if(nickCheck.creator === this.state.creator) {
firestore()
.collection('Rooms')
.doc(this.state.newRoomName)
.collection('Alpha')
.doc(taskIndex)
.set({
taskText: taskName,
})
.then(() => {
console.log('Task added')
})
}
else {
firestore()
.collection('Rooms')
.doc(this.state.newRoomName)
.collection('Beta')
.doc(taskIndex)
.set({
taskText: taskName,
})
.then(() => {
console.log('Task added')
})
} }
async componentDidMount() {
try {
const trytextx = await AsyncStorage.getItem('currentRoom')
const trial = JSON.parse(trytextx)
const maybe1 = await AsyncStorage.getItem('task1')
const string1 = JSON.parse(maybe1)
const maybe2 = await AsyncStorage.getItem('task2')
const string2 = JSON.parse(maybe2)
this.setState({newRoomName: trial})
if (string1 === false) {
this.setState({task1: false})
} else {
this.setState({ task1: string1 })
}
if (string2 === false) {
this.setState({task2: false})
} else {
this.setState({ task2: string2 })
}
} catch (e) {
console.log(e)
}
db.collection('Rooms')
.doc('RRN')
.collection('Alpha')
.get()
.then(snapshot => {
const tasks = []
snapshot.forEach(doc => {
const data = doc.data()
tasks.push(data)
})
this.setState({
finishedTasks: tasks
})
})
}
taskOne() {
if (this.state.task1) {
return (
<View>
<TaskOne />
<TouchableOpacity onPress={() => {
this.generateTask('Task1', 'First Task');
this.buttonTaskTwo()
}} raised='true' >
<View style={styles.buttonDone2}>
<Text style={styles.buttonText2}>
Выполнено
</Text>
</View>
</TouchableOpacity>
</View>
)
}
}
taskTwo() {
if (this.state.task2) {
return (
<View>
<TaskTwo />
<TouchableOpacity onPress={() => {
this.generateTask('Task2', 'Second Task');
this.buttonTaskThree()
}}>
<View style={styles.buttonDone2}>
<Text style={styles.buttonText2}>
Выполнено
</Text>
</View>
</TouchableOpacity>
</View>
)
}
}
CongratsMsg() {
if (this.state.allTasksComplete) {
return (
<View>
<CongratsMsg />
<TouchableOpacity onPress={this.lastButton}>
<View style={styles.buttonDone}>
<Text style={styles.buttonText2}>
Получить
</Text>
</View>
</TouchableOpacity>
</View>
)
}
}
lastMessage() {
if (this.state.zozo) {
return (
<View style={styles.MessageBG}>
<View >
{
this.state.finishedTasks.map(task => {
return (
<View>
<Text style={styles.MessageText}>*** {task.taskText} ***</Text>
</View>
)
})
}
</View>
</View>
)
}
}
lastButton = async () => {
this.setState({ zozo: true })
}
buttonTaskOne = async () => {
this.setState({ task1: true })
try {
await AsyncStorage.setItem('task1', JSON.stringify(true))
/* firestore()
.collection('Rooms')
.doc(this.state.newRoomName)
.set({
firstVisit: false
}, { merge: true }) */
} catch (e) {
console.log(e)
}
}
buttonTaskTwo = async () => {
this.setState({ task2: true })
try {
await AsyncStorage.setItem('task2', JSON.stringify(true))
} catch (e) {
console.log(e)
}
}
buttonCongrats = () => {
this.setState({ allTasksComplete: true })
}
render() {
return (
<ImageBackground source={bgImage} style={styles.backgroundContainer}>
<ScrollView style={styles.secondBg}>
<DefaultMsg />
<TouchableOpacity onPress={this.buttonTaskOne}>
<View style={styles.buttonDone}>
<Text style={styles.buttonText2}>
Начать!
</Text>
</View>
</TouchableOpacity>
<View>
{this.taskOne()}
</View>
<View>
{this.taskTwo()}
</View>
<View>
{this.CongratsMsg()}
</View>
<View>
{this.lastMessage()}
</View>
</ScrollView>
</ImageBackground>
)
}
}
I end up saving my state in firebase instead of asyncstorage.
Failed prop type: Invalid prop source supplied to Image I'm getting that error while uploading image.
I used image picker to pick image and made it component but it's not picking up source below is my code of image picker
FormImage.js
class FormImage extends Component {
state = {
hasCameraPermission: null,
};
async componentDidMount() {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
this.setState({ hasCameraPermission: status === "granted" });
}
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
});
if (!result.cancelled) {
this.setState({ image: result.uri });
this.props.formikProps.setFieldValue("image", result.uri);
}
};
render() {
return (
<TouchableWithoutFeedback onPress={this._pickImage}>
<View style={styles.container}>
{!this.props.image && (
<MaterialCommunityIcons
color={colors.medium}
name="camera"
size={40}
/>
)}
{this.props.image && (
<Image style={styles.image} source={{ uri: this.props.image }} />
)}
</View>
</TouchableWithoutFeedback>
);
}
}
I don't know why it's showing that error i will share my formik code below
AddPost.js
const validationSchema = Yup.object({
title: Yup.string().required().min(5).max(15).label("Title"),
des: Yup.string().required().min(15).max(200).label("Description"),
image: Yup.mixed(),
});
class AddPost extends Component {
render() {
return (
<Formik
initialValues={{ title: "", des: "", image: null }}
onSubmit={(values, actions) => {
this.props.addPost(values);
console.log(values);
}}
validationSchema={validationSchema}
>
{(value) => (
<KeyboardAvoidingView
behavior="position"
keyboardVerticalOffset={Platform.OS === "ios" ? 0 : 100}
>
<FormImage formikProps={value} image={value.values.image} />
<Text style={styles.error}>
{value.touched.image && value.errors.image}
</Text>
<TextInput
placeholder="Title"
onChangeText={value.handleChange("title")}
style={styles.input}
value={value.values.title}
onBlur={value.handleBlur("title")}
/>
below is my home screen code
home.js
class Home extends Component {
state = {
modal: false,
post: [
{
key: "1",
title: "A Good Boi",
des: "He's a good boi and every one know it.",
image: require("../assets/dog.jpg"),
},
{
key: "2",
title: "John Cena",
des: "As you can see, You can't see me!",
image: require("../assets/cena.jpg"),
},
],
};
addPost = (posts) => {
posts.key = Math.random().toString();
this.setState((prevState) => {
return {
post: [...prevState.post, posts],
modal: false,
};
});
};
render() {
return (
<Screen style={styles.screen}>
<Modal visible={this.state.modal} animationType="slide">
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.modalContainer}>
<AddPost addPost={this.addPost} />
</View>
</TouchableWithoutFeedback>
</Modal>
<FlatList
data={this.state.post}
renderItem={({ item }) => (
<>
<Card
title={item.title}
subTitle={item.des}
image={item.image}
onPress={() => this.props.navigation.navigate("Details", item)}
/>
</>
can someone tell me whats going on why it's not picking the source :/
I am using native base's input field and am trying to validate it using Formik and Yup. However, no validation is happening so far. It doesn't show any errors even if I type alphabets.
This code works (without Formik):
type EmailRegistrationProps = {};
interface FormValues {
friendEmail: string;
}
type AddFriendEmailPageProps = {
toggleShowPage: () => void;
showAddFriendEmailPage: boolean;
};
export const AddFriendEmailPage: React.FunctionComponent<AddFriendEmailPageProps> = ({
toggleShowPage,
showAddFriendEmailPage,
}) => {
const [friendEmail, setFriendEmail] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const validationSchema = emailValidationSchema;
const showAlert = () => {
Alert.alert('Friend Added');
}
useEffect(() => {
if (showAddFriendEmailPage) return;
setFriendEmail('');
}, [showAddFriendEmailPage]);
const _onLoadUserError = React.useCallback((error: ApolloError) => {
setErrorMessage(error.message);
Alert.alert('Unable to Add Friend');
}, []);
const [
createUserRelationMutation,
{
data: addingFriendData,
loading: addingFriendLoading,
error: addingFriendError,
called: isMutationCalled,
},
] = useCreateUserRelationMutation({
onCompleted : ( data: any) => {
showAlert();
}
});
const addFriend = React.useCallback(
(id: Number) => {
console.log('Whats the Id', id);
createUserRelationMutation({
variables: {
input: { relatedUserId: id, type: RelationType.Friend, userId: 7 },
},
});
},
[createUserRelationMutation],
);
const getFriendId = React.useCallback(
(data: any) => {
console.log('Email', friendEmail);
if (data) {
if (data.users.nodes.length == 0) {
setErrorMessage('User Not Found');
} else {
addFriend(Number(data.users.nodes[0].id));
}
}
},
[friendEmail, addFriend],
);
const [loadUsers] = useUsersLazyQuery({
onCompleted: getFriendId,
onError: _onLoadUserError,
});
const handleSubmit = React.useCallback(() => {
loadUsers({
variables: {
where: { email: friendEmail },
},
});
setFriendEmail('');
}, [loadUsers, friendEmail]);
}
return (
<Modal
visible={showAddFriendEmailPage}
animationType="slide"
transparent={true}>
<SafeAreaView>
<View style={scaledAddFriendEmailStyles.container}>
<View style={scaledAddFriendEmailStyles.searchTopContainer}>
<View style={scaledAddFriendEmailStyles.searchTopTextContainer}>
<Text
style={scaledAddFriendEmailStyles.searchCancelDoneText}
onPress={toggleShowPage}>
Cancel
</Text>
<Text style={scaledAddFriendEmailStyles.searchTopMiddleText}>
Add Friend by Email
</Text>
<Text style={scaledAddFriendEmailStyles.searchCancelDoneText}>
Done
</Text>
</View>
<View style={scaledAddFriendEmailStyles.searchFieldContainer}>
<Item style={scaledAddFriendEmailStyles.searchField}>
<Input
placeholder="Email"
style={scaledAddFriendEmailStyles.searchText}
onChangeText={(text) => setFriendEmail(text)}
value={friendEmail}
autoCapitalize="none"
/>
</Item>
<View style={scaledAddFriendEmailStyles.buttonContainer}>
<Button
rounded
style={scaledAddFriendEmailStyles.button}
onPress={() => handleSubmit()}
>
<Text style={scaledAddFriendEmailStyles.text}>
Add Friend{' '}
</Text>
</Button>
</View>
{/* </View>
)}
</Formik> */}
</View>
</View>
</View>
</SafeAreaView>
</Modal>
);
};
Now I am trying to add Formik:
EDIT:
export const AddFriendEmailPage: React.FunctionComponent<AddFriendEmailPageProps> = ({
toggleShowPage,
showAddFriendEmailPage,
}) => {
const initialValues: FormValues = {
friendEmail: '',
};
//const [friendEmail, setFriendEmail] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const validationSchema = emailValidationSchema;
const showAlert = () => {
Alert.alert('Friend Added');
}
useEffect(() => {
if (showAddFriendEmailPage) return;
initialValues.friendEmail = '';
}, [showAddFriendEmailPage]);
const _onLoadUserError = React.useCallback((error: ApolloError) => {
setErrorMessage(error.message);
Alert.alert('Unable to Add Friend');
}, []);
const [
createUserRelationMutation,
{
data: addingFriendData,
loading: addingFriendLoading,
error: addingFriendError,
called: isMutationCalled,
},
] = useCreateUserRelationMutation({
onCompleted : ( data: any) => {
showAlert();
}
});
const addFriend = React.useCallback(
(id: Number) => {
console.log('Whats the Id', id);
createUserRelationMutation({
variables: {
input: { relatedUserId: id, type: RelationType.Friend, userId: 7 },
},
});
},
[createUserRelationMutation],
);
const getFriendId = React.useCallback(
(data: any) => {
console.log('Email', friendEmail);
if (data) {
if (data.users.nodes.length == 0) {
console.log('No user');
setErrorMessage('User Not Found');
Alert.alert('User Not Found');
} else {
console.log('ID', data.users.nodes[0].id);
addFriend(Number(data.users.nodes[0].id));
}
}
},
[friendEmail, addFriend],
);
const [loadUsers] = useUsersLazyQuery({
onCompleted: getFriendId,
onError: _onLoadUserError,
});
const handleSubmit = React.useCallback((
values: FormValues,
helpers: FormikHelpers<FormValues>,
) => {
console.log('Submitted');
loadUsers({
variables: {
where: { email: values.friendEmail },
},
});
//setFriendEmail('');
values.friendEmail = '';
}, [loadUsers, initialValues.friendEmail]);
}
return (
<Modal
visible={showAddFriendEmailPage}
animationType="slide"
transparent={true}>
<SafeAreaView>
<View style={scaledAddFriendEmailStyles.container}>
<View style={scaledAddFriendEmailStyles.searchTopContainer}>
<View style={scaledAddFriendEmailStyles.searchTopTextContainer}>
<Text
style={scaledAddFriendEmailStyles.searchCancelDoneText}
onPress={toggleShowPage}>
Cancel
</Text>
<Text >
Add Friend by Email
</Text>
<Text>
Done
</Text>
</View>
<View style={scaledAddFriendEmailStyles.searchFieldContainer}>
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={validationSchema}>
{({
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
values,
}) => (
<Field
component={Input}
placeholder="Email"
onChangeText={handleChange('friendEmail')}
onBlur={handleBlur('friendEmail')}
value={values.friendEmail}
autoCapitalize="none"
/>
)}
</Formik>
<View >
<Button
onPress={() => handleSubmit()}
>
<Text >
Add Friend{' '}
</Text>
</Button>
</View>
</View>
</View>
</View>
</SafeAreaView>
</Modal>
);
};
Currently, this is not working for me. I want to keep using my old handleSubmit that I am using via the onPress of the button. But now I don't know how to pass the values, helpers into this handleSubmit:
onPress={() => handleSubmit()}
I get Expected 2 arguments, but got 0.
But if I try to pass values, helpers these names are not found.
Similarly, I am using
[friendEmail, addFriend],
at the end of getFriendId. This was working properly if I just use setState without formik validation etc. But now friendEmailcan't be found. I am just unable to merge Formik properly in such a way that I can also reset values like I can do while using useState.
Formik requires you to utilize the <Field /> component for validation.
<Field /> will automagically hook up inputs to Formik. It uses the name attribute to match up with Formik state. <Field /> will default to an HTML <input /> element.
You can set custom components via the component prop.
In your case, for example:
<Field
component={Input}
name="phoneNumber"
placeholder="Phone Number"
onChangeText={handleChange}
onBlur={handleBlur}
type='tel'
value={values.phoneNumber}
/>
Update
Ahh, my bad, I updated the onChangeText and onBlur to reflect the changes. In the current implementation you're actually running the "handle" events on load rather than when the even occurs. If you name the input it should pass that information along automagically. Also, you should set a type for the input. I've updated the above example for all of these updates.
Been working on finding a way to delete the clicked on document using React Native and Cloud Firestore. I can't figure out a way to get the document id and then use it in my code to replace the value of deleteItemId. Any ideas?
My collection with a document showing:
My code:
componentDidMount(){
this.getItems();
const { currentUser } = firebase.auth();
this.setState({ currentUser });
}
getItems = async () => {
this.setState({ refreshing: true });
this.unsubscribe = await this.ref.onSnapshot((querySnapshot) => {
const todos = [];
querySnapshot.forEach((doc) => {
todos.push({
tips: doc.data().tips,
date: doc.data().date,
user: doc.data().user,
like: doc.data().like
})
})
this.setState({
refreshing: false,
getData: todos
})
})
}
deletePost = () => {
const deleteItemId = "SELECTED DOCUEMNT ID HERE";
firestore.collection("tips").doc(deleteItemId).delete().then(function() {
alert("deleted")
}).catch(function(error) {
alert("Error removing document: ", error);
});
}
renderItem = ({ item, index }) => {
let date = item.date;
return (
<View style={styles.tips}>
<View style={styles.wrapper}>
<View style={styles.profilePicture}>
<View></View>
</View>
<View style={styles.right}>
<Text style={styles.username}>#{item.user}</Text>
<Text style={styles.date}>{ moment(item.date).fromNow() }</Text>
</View>
</View>
<Text style={styles.text}>{item.tips}</Text>
<View style={styles.bar}>
<Text><Icon onPress={() => this.like()} style={styles.heart} type="Octicons" name="heart" /> {item.like}</Text>
<Text onPress={() => {
this.setModalVisible(true);
}}><Icon style={styles.comment} type="FontAwesome" name="comment-o" /> {item.replies}</Text>
<Text onPress={() => this.deletePost()}><Icon style={styles.settings} type="Octicons" name="kebab-vertical" /></Text>
</View>
</View>
)
}
Every time you push a TODO to todos, make sure to also include the document ID:
todos.push({
id: doc.id,
tips: doc.data().tips,
date: doc.data().date,
user: doc.data().user,
like: doc.data().like
})
Then when you render a TODO, you include the ID in the rendering output of the eleent:
<Text onPress={() => this.deletePost(styles.id)}>