How to keep or store data in forms input fields. after form submitted in react native - forms

I am new to react native. I have created a form. Now all work fine. But when I submit form. and after I clear expo app from recent apps and start again. then I want to show previous form filled data. in each input.
and user will able to edit it. so in short I want store or keep all input data in input field. so please help. thanks. is it possible. if yes then please help
here is my code
constructor(props) {
super(props);
this.state={
Place:"",
};
validateInputs = (event) => {
if (!this.state.Place.trim())
{
this.setState({ PlaceError: 'Field Should Not Be An Empty' })
return;
}
handlePlace = (text,event) => {
this.setState({ PlaceError: '' })
this.setState({ Place: text })
}
render(){
return (
<TextInput maxLength={50} placeholder="Place" style={styles.inputStyle}
onChangeText={this.handlePlace} defaultValue={this.state.Place} />
<Text style={{color :'red'}}>{this.state.PlaceError}</Text>

Related

React Query - How to find out which mutation led to refetching of query

In my simple ToDo app i am using useQuery() to fetch ToDo's from the server as well as useMutation() to create, update and delete ToDo's. When a mutation is successful, i invalidate the query so it gets refetched.
If i press the delete button of a ToDo item, i want the corresponding button to show a loading spinner until the mutation is done and the new ToDo's have been fetched. For that purpose i am using the useIsFetching() hook in my components, which works fine. However, here is the problem:
If i now execute a mutation, every button (meaning the "Delete" button as well as the "Submit" and "Save changes" button to post or update a ToDo) will show the loading spinner instead of just the one that i pressed. This makes sense since they all depend on the same value of useIsFetching(). I need a way to figure out which mutation led to the refetching of the query so i can conditionally render the loading spinner for the appropriate button. This seems to be a very common problem for me yet i cannot find a (not overcomplicated) solution for it. Is there something i'm missing?
The solution Ahmed Sbai said above is good (you can use state instead of local variables), and here is another approach for you.
You can check condition based on isLoading in the object returned from useMutation().
Updated: As written in this TkDodo's blog, the "magic" is here:
If you want your mutation to stay in loading state while your related queries update, you have to return the result of invalidateQueries() from the callback.
Therefore, you won't need to use the useIsFetching() hook, too.
function App() {
const addMutation = useMutation({
mutationFn: (newTodo) => {
return axios.post('/todos', newTodo)
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: ['todos'],
})
}
})
const updateMutation = useMutation({
mutationFn: (id, data) => {
return axios.patch(`/todos/${id}`, data)
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: ['todos'],
})
}
})
const deleteMutation = useMutation({
mutationFn: (id) => {
return axios.delete(`/todos/${id}`)
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: ['todos'],
})
}
})
return (
<div>
{/* ... */}
<button
onClick={() => addMutation.mutate(...)}
loading={addMutation.isLoading}
>
Submit
</button>
<button
onClick={() => updateMutation.mutate(...)}
loading={updateMutation.isLoading}
>
Save changes
</button>
<button
onClick={() => deleteMutation.mutate(...)}
loading={deleteMutation.isLoading}
>
Delete
</button>
</div>
)
}
If you want any further information, please read more in Docs.
You can simply create a variable var loadingType = 0 and update its value each time the user click on a button for example if the delete button is clicked then loadingType = 1 if update button loadingType = 2, etc. Then based on the value of loadingType you know which loading spinner you have to use.

React Native - How to validate Textinput correclty?

How to validate Textinput correclty? I want to validate my form correctly with custom form validation and after validation display errors in Text component, but how? Please, guys show me example!
install react-native-snackbar to show error messages.
import React, { Component } from 'react';
import { View, Text, TextInput } from 'react-native';
import Snackbar from 'react-native-snackbar';
export default class LoginPasswordScreen extends Component {
constructor(props) {
super(props);
this.state = {
password: ''
}
}
validate = () => {
//include your validation inside if condition
if (this.state.password == "") {
() => {
setTimeout(() => {
Snackbar.show({
title: 'Invalid Credintials',
backgroundColor: red,
})
}, 1000);
}
}
else {
Keyboard.dismiss();
// navigate to next screen
}
}
render() {
return (
<View>
<TextInput
returnKeyType="go"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
autoFocus={true}
onChangeText={(password) => this.setState({ password })}
/>
<TouchableOpacity>
<Text onPress={this.validate}>Next</Text>
</TouchableOpacity>
</View>
);
}
}
Every field, you have to do a comparison and show the error message and as I see there is no direct form validation even though there is form component available in react native.
In One of my react native project, I added a form and later on click of Submit, I had written one validate function to check all my inputs.
For this, I used one nice javascript library-
npm library- validator
And for showing error message, you can use, Toast, ALert or Snackbar
Would be nice if you provide some thoughts or code on how you would think it can be approached. But the way i did it was pretty simple, on my component state i got the following object:
this.state = {
loading: false,
username: {
text: '',
valid: false
},
password: {
text: '',
valid: false
},
isLoginValid: false
};
Then on the TextInput for username, i would first, bind its value to this.state.username.text, also, during onChangeText I just do a simple validation of the field, if the form is quite big, you may have a switch(fieldtype) where you have for each field, what treatment you want to apply a.k.a validation.
onChangeText={ (text) => { this.validateInput(text, 'username')}} (username would be the form input on the state object)
For instance, for username you want only to be length != 0 and length <= 8 characters, for email you may run a RegExp() with the email validation and also its length, for password a different logic, etc... after that i just simply save the state for that field input and if it's valid or not. Like this:
validateInput(text, fieldname) {
let stateObject = {
text: text,
valid: text.length !== 0
}
this.setState({ [fieldname]: stateObject }, () => {
this.checkValidation();
});
}
In checkValidation I check for all the input fields and if every one is valid, i set formValid to true.
This formValid would for example, allow the user to tap on the "Login" button otherwise it applies an opacity of 0.5 to it and disables it.
The rest you may guess, is just playing around with the valid variables of each field to what you want to display and what not.
In a Register form I also added an X or a "Tick" icon if the input text field is ok or not. Let your imagination guide you.
Hope it helps.

converse.js : How can I pre-populate the Username field in the login form

I have tried this:
<script>
converse.initialize({
websocket_url: 'wss://xxx.xxx.xxx/websocket',
jid:'xxxxx#xxxx.xxx.xxx',
show_controlbox_by_default: true,
view_mode: 'overlayed' });
</script>
I was hoping this would display a login form with the Username field already populated with the value given in jid. However converse.js still displays an empty field with the default placeholder.
I'm using "https://cdn.conversejs.org/4.0.1/dist/converse.min.js"
OK so I'm not a javascript programmer but the solution I've come up with is to modify the renderLoginPanel() function and add the following
renderLoginPanel() {
this.el.classList.add("logged-out");
if (_.isNil(this.loginpanel)) {
this.loginpanel = new _converse.LoginPanel({
'model': new _converse.LoginPanelModel()
});
const panes = this.el.querySelector('.controlbox-panes');
panes.innerHTML = '';
panes.appendChild(this.loginpanel.render().el);
this.insertBrandHeading();
} else {
this.loginpanel.render();
}
/* ***Add this line to pre-populate the username field*** */
document.getElementById("converse-login-jid").value = _converse.jid;
this.loginpanel.initPopovers();
return this;
},
I'd be interested to hear if this is anywhere near a good solution.

React JS - Composing generic form with dynamic childrens

I am just a beginner in reactjs. I feel so good about using it, but I have been stuck for a while on a concept that I wanna implement, that would solve me whole lot of other issues.
Concept
I want to create a form component that can have dynamic children passed to it. It won't be a specific form component like LoginForm, ContactForm etc. Rather it would just be Form.
Approach 1
class LoginPage extends Rect.Component {
constructor(props) {
super(props)
this.submit = this.submit.bind(this);
this.validate = this.validate.bind(this);
this.reset = this.reset.bind(this);
}
submit(...args) {
this.refs.form.submit(args);
}
validate(...args) {
this.refs.form.validate(args);
}
reset(...args) {
this.refs.form.reset(args);
}
render() {
return (
<LoginPage>
<Form ref="form" onSubmit={this.submit}>
<Input value="email" pattern="/email_pattern/" onValidate={this.validate} />
<Input value="password" pattern="/password_pattern/" onValidate={this.validate} />
<Button value="submit" action={this.submit} />
<Button value="reset" action={this.reset} />
</Form>
</LoginPage>
}
}
Input onChange calls the validate function that just passes on the args to the Form's validate function. For the Form to know if all it's children's are validated. I pass isValid and targetInputComponent as args to the form.
Button Submit onClick calls the submit function likewise and LoginPage (acts as middleware) passes the call to the Form component. Form check it's state for inValid inputs etc.
Button Reset onClick call is passed to the Form component likewise. But how do the form handle this reset functionality? Input's value is controlled by LoginPage. Form can't change the input's value.
Aproach 2
What I did was add the input's data and validity to the LoginPage state. Now both Form and Inputs just call the Login Page to update it's state. Form and Inputs components are using this state as prop.
class LoginPage extends React.Component {
constructor(props) {
super(props)
this.state = {
formSchema: this.initSchema()
}
this.validateField = this.validateField.bind(this);
this.submitForm = this.submitForm.bind(this);
}
initSchema() {
const formSchema = {
email: {
isValid: false,
value: ''
},
password: {
isValid: false,
value: ''
},
password2: {
isValid: false,
value: ''
}
}
return formSchema;
}
validateField(dataObj, targetComponent) {
const formSchema = Object.assign(this.state.formSchema, dataObj);
this.setState({ formSchema })
}
submitForm(isSuccess) {
console.log(isSuccess);
console.log('Form Submit: ', this.state.formSchema);
throw new Error('Submition Error');
}
reset() {
// Loop through each object in formSchema and set it's value to empty, inputs will validate themselves automatically.
}
render() {
return <div>
<Form ref="formLogin" className="auth-form" onSubmit={this.submitForm} formSchema={this.state.formSchema}>
<h1>
Switch Accounts
</h1>
<Input type="email" name="email" onValidate={this.validateField} value={this.state.formSchema.email.value}
isValid={this.state.formSchema.email.isValid}/>
<Input type="password" name="password" onValidate={this.validateField} value={this.state.formSchema.password.value}
isValid={this.state.formSchema.password.isValid}/>
<Button value="Submit" type="submit" />
</Form>
</div>
}
}
Problem
This approach is making the LoginPage Component quite messy. How will the LoginPage component will handle the forms if I have more than 1 form on the page? There will be even more features to LoginPage like Lists, Grid, Modals etc. This LoginPage shouldn't Handle these situations. Form should be responsible for all the inputs, submition, etc functionality. This form component should be generic to all types of forms. I don't want to create feature forms like LoginForm, ContactForm, etc.
The solution to this issue will aid me a lot in whole lot of other components.
Your approach 2 is the standard way of handling this problem.
Why wouldn't you create a separate <LoginForm>? Doing so can abstract the fields and validation away from other unrelated functionalities on your page.
If later you need a <ContactForm> or any other type of form, it will have different fields and different validation logic, meaning you'll need to build it out regardless.
Short of using something like redux, you're on the right track.
There are numerous ways to make ‘generic’ forms. But in my opinion it is best to keep the forms seperatly.
The reason why I say this, is because one way or another, you still have to implement various validation condition for most fields, such as e-mailaddress or what ever you will use.
I think the best approach is to make the components individual. For example: make a TextField component that handles input stuff like validation. Maybe a Button component for submit and callbacks.
So my advise: keep the forms seperatie. No need to overthink and lose useful time in trying to make things ‘pretty’.
Goodluck!

ReactRouter doesn't redirect correctly after flux notification

I'm a newbie in react and flux. As a matter of facts I stumbled into the following problem when using ReactRouter (2.4)
I'm using hashHistory and I need to redirect to the "/" page when I'm in "/login" page after a successful login attempt.
Router
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={ErasmusPage} onEnter={authCheck}></IndexRoute>
<Route path="login"component={withRouter(LoginPage)}></Route>
</Route>
</Router>, app);
Login Page
constructor() {
super();
this.notifyLogin = this.notifyLogin.bind(this);
this.state = {
email: "",
password: ""
};
}
componentWillMount() {
authStore.on("login", this.notifyLogin);
}
componentWillUnmount() {
authStore.removeListener("login", this.notifyLogin);
}
notifyLogin() {
this.props.router.push('/');
}
...
handleSubmit(e) {
e.preventDefault();
let data = {
email: this.state.email,
password: this.state.password
};
AuthActions.authenticate(data);
}
...
The flow is:
after one submits, authActions and Store elaborate the datas (ajax call involved).
If the login attempt is all right, AuthStore emits a "login" signal...
... so i can execute notifyLogin().
The problem is: this.props.router.push('/') doesn't redirect properly, it changes the URL but not the page (it looks like the state refresh doesn't get triggered).
Weird thing is, if I put this.props.router.push('/') in the handleSubmit function the redirect works perfectly.
Any idea of what's going on?
This has worked well for me so far:
Wrap your app in an component that contains login/auth state.
on render, if not logged in, render the login page component, otherwise, render the app component
render() {
const { isAuthorized, authError} = this.props;
if (isAuthorized) {
return (<Layout />);
}
return (<Login authError={authError}/>);
}