I'm trying to append elements to my in react native, using this.state.
But it doesn't return the Text element. My View stays empty...can anyone help?
I'm using this as a reference.
function App() {
const {register, setValue, handleSubmit} = useForm();
this.state = {message: []};
useEffect(() => {
register('author');
register('message');
}, [register]);
var socket = io.connect('http://localhost:3000/');
const onSubmit = data => {
renderMessage(data);
socket.emit('sendMessage', data);
};
socket.on('receivedMessage', function (data) {
renderMessage(data);
});
const renderMessage = data => {
this.state.message.push(data);
};
return (
<View style={styles.sectionContainer}>
<TextInput
style={styles.sectionAuthor}
placeholder="author"
onChangeText={text => setValue('author', text)}
/>
<View style={styles.sectionDescription}>
{this.state.message.map(item => (
<Text>{item.author}</Text>
))}
</View>
<TextInput
style={styles.sectionMessage}
placeholder="Message"
onChangeText={text => setValue('message', text)}
/>
<Button onPress={handleSubmit(onSubmit)} title="Enviar" />
</View>
);
}
You are mixing functional component and class component.
You should use useState to manage state instead of this.state
const [messages,setMessages] = useState([]);
const renderMessage = data => {
setMessages([...messages, data]);
};
then in your rendering, it should use messages.
{messages.map(item => (
<Text>{item.author}</Text>
))}
function App() {
const {register, setValue, handleSubmit} = useForm();
const [message,setMessage] = useState([]);
useEffect(() => {
register('author');
register('message');
}, [register]);
var socket = io.connect('http://localhost:3000/');
const onSubmit = data => {
renderMessage(data);
socket.emit('sendMessage', data);
};
socket.on('receivedMessage', function (data) {
renderMessage(data);
});
const renderMessage = data => {
let new_msg = [...message];
new_msg.push(data);
setMessage(new_msg);
};
return (
<View style={styles.sectionContainer}>
<TextInput
style={styles.sectionAuthor}
placeholder="author"
onChangeText={text => setValue('author', text)}
/>
<View style={styles.sectionDescription}>
{this.state.message.map(item => (
<Text>{item.author}</Text>
))}
</View>
<TextInput
style={styles.sectionMessage}
placeholder="Message"
onChangeText={text => setValue('message', text)}
/>
<Button onPress={handleSubmit(onSubmit)} title="Enviar" />
</View>
);
}
I am working in react-native and used 'react-hook-forms' for dynamic form creation,
but it's returning empty data object, but it should return values typed in input fields. onSubmit function gives an empty values. Please check my below code. please help me...
//InputBox.js. this my dynamic element where I want access its values in AddDirectries.js
import { useForm, Controller } from 'react-hook-form'
const InputBox = ({ fields, fieldName }) => {
let { control } = useForm();
return (
<>
{ fields.field_unit == null ? <Text style={styles.formsTitle}>{fieldName}</Text> :
<Text style={styles.formsTitle}> {fieldName + "(" + fields.field_unit + ")"}</Text>
}
{[...Array(fields.field_count)].map((item, index) => {
return (
<Controller
control={control}
name={'j' + index}
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
onChangeText={value => onChange(value)}
value={value}
></TextInput>
)}
/>
)
})
})
</>)
}
export default InputBox;
//AddDirectries.js here I want access TextInput values.
import { useForm } from 'react-hook-form'
import Element from '../components/Element'
import FormJson from '../../From.json'
export const AddDirectories = (props) => {
let { handleSubmit, control, formState: { errors } } = useForm();
const [elements, setElements] = useState([]);
const [keys, setKeys] = useState([]);
const onSubmit = data => console.log(data);
useEffect(() => {
setKeys(Object.keys(FormJson))
setElements(FormJson)
}, [])
return (
<View style={[styles.mainScreen, { backgroundColor: 'transparent' }]}>
{
keys.map((fieldName, index) => {
return <Element fieldName={fieldName} fields={elements[keys[index]]} key={index}></Element>
})
}
<TouchableOpacity onPress={handleSubmit(onSubmit)} style={[styles1.addButton, { marginBottom: 30 }]}>
<Text>ADD</Text>
</TouchableOpacity>
</View>
)
}
//Element.js
const Element = ({fieldName, fields }) => {
switch (fields.field_type) {
case ("TextField"):
return (<InputLocation fields ={fields} fieldName={fieldName} />)
default: return null;
}
}
export default Element;
I just had the same problem.
instead of :
<Controller
control={control}
name={'j' + index}
defaultValue=""
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
onChangeText={value => onChange(value)}
value={value}
></TextInput>
)}
/>
spread field into textinput:
<Controller
control={control}
name={'j' + index}
defaultValue=""
render={({ field }) => (
<TextInput
{...field}
onChangeText={value => field.onChange(value)}
value={field.value}
></TextInput>
)}
/>
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 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.
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)}>