React hooks not keeping data - javascript

I've been trying to learn React hooks in order to start building a personal project but ran into a few road blocks. Currently, when I do an axios request, the page resets and no data is shown.
In order to make sure it was working the correct way, I made a class version and was able to retrieve the data plus upload it to state with setState.
import React, { useState } from "react";
import axios from "axios";
const Form = () => {
const [signup, setForm] = useState({ username: "", email: "", password: "" });
const [user, setUser] = useState({ user: "" });
const submit = () => {
axios.get("/api/users").then(user => {
setUser({ user });
});
};
return (
<div>
{user.username}
<h1>This is the Landing Page!</h1>
<form onSubmit={submit}>
<input
type="text"
placeholder="Enter Username"
value={signup.username}
onChange={e => setForm({ ...signup, username: e.target.value })}
/>
<input
type="text"
placeholder="Enter Email"
value={signup.email}
onChange={e => setForm({ ...signup, email: e.target.value })}
/>
<input
type="password"
placeholder="Enter Your Password"
value={signup.password}
onChange={e => setForm({ ...signup, password: e.target.value })}
/>
<input type="submit" value="Submit" />
</form>
</div>
);
};
The class option works
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
user: ""
};
}
loadData = () => {
console.log(this.state);
axios.get("/api/users").then(user => {
console.log(user.data);
debugger;
this.setState({ user });
});
};
render() {
return (
<div>
<h1>This is the header</h1>
<button onClick={this.loadData}>This is a button</button>
</div>
);
}
}
What I'm expecting is for the data to persist. It does appear in console.log but even that disappears within a few seconds as if the entire page keeps reloading. When I do input typing it works but not on a axios call.

Change your onSubmit function to the following to avoid a page reload.
const submit = (e) => {
e.preventDefault();
axios.get("/api/users").then(user => {
setUser({ user });
});
};

You want to disable the browser from submitting the form by adding an event.preventDefault(), and let your submit() function send that request with Axios (or fetch) instead:
const submit = event => {
event.preventDefault();
...
}

Related

How to submit React form without changing value in field

I am using a React library called Kendo React in order to create a simple form. What I want is to be able to submit my form when the data is changed by clicking on the Load new user button, however the current behavior is that when I click the button and populate with data the submit button does not submit form until I manually change the value of the field, why and how can I just submit the form immediately after I update the data and the button is enabled? Here is my code:
import React from 'react';
import ReactDOM from 'react-dom';
import { Form, Field, FormElement } from '#progress/kendo-react-form';
import { Error } from '#progress/kendo-react-labels';
import { Input } from '#progress/kendo-react-inputs';
const emailRegex = new RegExp(/\S+#\S+\.\S+/);
const emailValidator = value => emailRegex.test(value) ? "" : "Please enter a valid email.";
const EmailInput = fieldRenderProps => {
const {
validationMessage,
visited,
...others
} = fieldRenderProps;
return <div>
<Input {...others} />
{visited && validationMessage && <Error>{validationMessage}</Error>}
</div>;
};
const App = () => {
const handleSubmit = dataItem => alert(JSON.stringify(dataItem, null, 2));
const [user, setUser] = React.useState({
firstName: 'John',
lastName: 'Smith',
email: 'John.Smith#email.com'
});
const loadNewUser = () => {
setUser({
firstName: 'NewFirstName',
lastName: 'NewLastName',
email: 'NewEmails#email.com'
});
};
return <React.Fragment>
<button className='k-button' onClick={loadNewUser}> Load new user </button>
<hr className='k-hr' />
<Form onSubmit={handleSubmit} initialValues={user} key={JSON.stringify(user)} render={formRenderProps => <FormElement style={{
maxWidth: 650
}}>
<fieldset className={'k-form-fieldset'}>
<legend className={'k-form-legend'}>Please fill in the fields:</legend>
<div className="mb-3">
<Field name={'firstName'} component={Input} label={'First name'} />
</div>
<div className="mb-3">
<Field name={'lastName'} component={Input} label={'Last name'} />
</div>
<div className="mb-3">
<Field name={"email"} type={"email"} component={EmailInput} label={"Email"} validator={emailValidator} />
</div>
</fieldset>
<div className="k-form-buttons">
<button type={'submit'} className="k-button" disabled={!formRenderProps.allowSubmit}>
Submit
</button>
</div>
</FormElement>} />
</React.Fragment>;
};
ReactDOM.render(<App />, document.querySelector('my-app'));
this is normal because when you use setUser a render event is triggered and not a change event on your inputs, so to submit your form on every loadNewUser click, you can add the submit logic to loadNewUser method after setUser. The last option is better as you avoid triggering the submit logic before setUser call inside loadNewUser.
Or use UseEffect to perform the submit on each change to the user object.
useEffect(() => {
console.log(user);
}, [user]);
Here is a simple example ( without Kendo ) :
import { useEffect, useState } from "react";
export default function App() {
const [user, setUser] = useState(null);
const loadNewUser = () => {
setUser({ name: "alan" });
};
const onchange = (event) => {
setUser({
...user,
name: event.target.value
});
};
const submit = () => {
console.log("submit", user);
};
useEffect(() => {
user && submit();
}, [user]);
return (
<div className="App">
<button onClick={loadNewUser}>Load new user</button>
<input
type="text"
onChange={onchange}
value={user != null ? user.name : ""}
/>
<button onClick={submit}>submit</button>
</div>
);
}

from controlled form into hook

Hello this is my first question here and I am just a beginner in Reactjs I need your explanation, please
the code is about Controlled Form wrote in-class component using "this.state".
I was trying time to turn it into a functional component using hooks with the same results
1- onSubmit render text on the screen
2- reset input into ""
the problem is no results going write and instead I got [object, Object] in the search
this is code
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
event.preventDefault()
this.setState({
submit: this.state.input,
input:''
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type='text'
value={this.state.input}
onChange={this.handleChange}
/>
<button type='submit'>Submit!</button>
</form>
<h1>{this.state.submit}</h1>
</div>
);
}
}
The code at codesandbox for fast access
please can you tell me how to solve it?
thank you
Here is what you need https://codesandbox.io/s/new-leftpad-1n0yy, you can compare your current MyForm with Form to understand the difference, but I suggest to check documentation deeper
Here is the answer by Artem Matiushenko he added the second component using useState, useCallback
now we can compare the two types for controlled form
import React, { useCallback, useState } from "react";
import "./styles.css";
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "",
submit: "",
};
}
handleChange = (event) => {
this.setState({
input: event.target.value,
});
};
handleSubmit = (event) => {
event.preventDefault();
this.setState({
submit: this.state.input,
input: "",
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.input}
onChange={this.handleChange}
/>
<button type="submit">Submit!</button>
</form>
<h1>{this.state.submit}</h1>
</div>
);
}
}
//using hooks------->
function Form() {
const [value, setValue] = useState("");
const [submitedValue, setSubmitedValue] = useState();
const handleOnChange = useCallback(({ target }) => {
setValue(target.value);
}, []);
const handleOnSubmit = useCallback(
(event) => {
event.preventDefault();
setSubmitedValue(value);
setValue("");
},
[value]
);
return (
<div>
<form onSubmit={handleOnSubmit}>
<input type="text" value={value} onChange={handleOnChange} />
<button type="submit">Submit!</button>
</form>
<h1>{submitedValue}</h1>
</div>
);
}
export default function App() {
return (
<div className="App">
<h1>Class Component Form</h1>
<h2>controlled form</h2>
<MyForm />
<Form />
</div>
);
}
here is mine after I understood how useSate works
const MyForm = () => {
const [input, setInput] = useState("");
const [submitText, setSubmitText] = useState("");
const handleChange = (event) => {setInput(event.target.value)};
const handleSubmit = (event) => {
event.preventDefault();
setSubmitText(input);
setInput("");
};
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" value={input} onChange={handleChange} />
<button type="submit">Submit!</button>
</form>
<h1>{submitText}</h1>
</div>
);
};

What is best way to create forms in react?

I am beginner in react. I have following code:
import React, { useState, useEffect } from 'react';
import { Card, Form, Button } from 'react-bootstrap';
import Axios from 'axios'
export function StudentForm({ student, onSuccess, onError, setState }) {
const url = `http://localhost:9899/api/StudentData`;
const intialStudent = { Firstname: '', Middlename: '', Lastname: '', DOB: '', Gender: '' };
const [Student, setStudent] = useState(intialStudent);
useEffect(() => {
setStudent(student ? student : intialStudent);
}, [student]);
const SaveData = function (studentData) {
if (student._id) {
Axios.post(url, { ...studentData }, { headers: { 'accept': 'application/json' } })
.then(res => {
setState(null);
onSuccess(res);
})
.catch(error => {
alert('Error To Edit data');
});
}
else {
Axios.post(url, studentData, { headers: { 'accept': 'application/json' } })
.then(res => {
setState(null);
onSuccess(res);
})
.catch(err => onError(err));
}
}
return (
<Card>
<Card.Header><h5>{student ? "Edit" : "Add"} Student</h5></Card.Header>
<Card.Body>
<Form onSubmit={(e) => { e.preventDefault(); SaveData(Student); }}>
<Form.Group><Form.Control type="text" name="Firstname" placeholder="Firstname" value={Student.Firstname} onChange={e => { setStudent({ ...Student, Firstname: e.target.value }) }} /></Form.Group>
<Form.Group><Form.Control type="text" name="Middlename" placeholder="Middlename" value={Student.Middlename} onChange={e => setStudent({ ...Student, Middlename: e.target.value })} /></Form.Group>
<Form.Group><Form.Control type="text" name="Lastname" placeholder="Lastname" value={Student.Lastname} onChange={e => setStudent({ ...Student, Lastname: e.target.value })} /></Form.Group>
<Form.Group><Form.Control type="date" name="DOB" placeholder="DOB" value={Student.DOB} onChange={e => setStudent({ ...Student, DOB: e.target.value })} /></Form.Group>
<Form.Group><Form.Control type="text" name="Gender" placeholder="Class" value={Student.Gender} onChange={e => setStudent({ ...Student, Gender: e.target.value })} /></Form.Group>
<Button variant="primary" type="submit">Submit</Button>
</Form>
</Card.Body>
</Card>
);
}
In above code I am setting state on change event on each field. So it will render again and again when I change any of the field.If it is large form so it may take a lot of time to re-render so is there a better way to create to handle this kind of situation, or any best practices for using forms with react?
You can use only one Function for all onChanges. Looks like this;
<Form.Group>
<Form.Control
type="text"
name="Firstname"
placeholder="Firstname"
value={Student.Firstname}
onChange={handleChange}
/>
</Form.Group>
And this is your handleChange function;
const handleChange = e => {
const {name, value} = e.target
setValues({...values, [name]: value})
}
This is your state;
const [values, setValues] = useState({
Firstname: "",
Middlename: "",
Lastname: "",
DOB: "",
Gender: ""
})
I think this way is more effective with less code.
Managing forms in react is a task complex enough to delegate it to a library.
Alo, big forms are not a good candidate for functional components because the problems that you outlined. You can, of course, spend the time to tune it up, but I think the effort may not worth the benefit.
My personal recommendation is to try one of the many react form libraries out there. One that I personally like is Formik
If you want to manage the form yourself I recommend to encapsulate the form on stateful component and use the key property for easier reset when you need it.
Another alternative will be the usage of memoization, for example using react.memo. But that will not guarantee success unless your data has the proper shape. This means, simple values that can be compared between themselves, not arrays, not functions, not objects.
You have to re render the form when an input changed but you don't need to re render every input when you make sure the onChange function doesn't change reference on every render and your input is a pure component (using React.memo for functional component and inherit from React.PureComponent for class components).
Here is an example of optimized inputs.
const {
useEffect,
useCallback,
useState,
memo,
useRef,
} = React;
function App() {
return <StudentForm />;
}
//put initial student here so it doesn't change reference and quits the linter
// in useEffect
const initialStudent = {
Firstname: '',
Middlename: '',
};
function StudentForm({ student }) {
const [Student, setStudent] = useState(initialStudent);
//useCallback so onChange is not re created and causes re rendering
// of components that didn't change
const onChange = useCallback(
(key, value) =>
setStudent(student => ({ ...student, [key]: value })),
[]
);
useEffect(() => {
setStudent(student ? student : initialStudent);
}, [student]);
const SaveData = function(studentData) {
console.log('saving data:', studentData);
};
return (
<form
onSubmit={e => {
e.preventDefault();
SaveData(Student);
}}
>
<InputContainer
type="text"
name="Firstname"
placeholder="Firstname"
value={Student.Firstname}
stateKey="Firstname" //provide state key
onChange={onChange}
/>
<InputContainer
type="text"
name="Middlename"
placeholder="Middlename"
value={Student.Middlename}
stateKey="Middlename"
onChange={onChange}
/>
<button type="submit">Submit</button>
</form>
);
}
//make this a pure component (does not re render if nothing changed)
const InputContainer = memo(function InputContainer({
type,
name,
placeholder,
value,
onChange,
stateKey,
}) {
const rendered = useRef(0);
rendered.current++;
return (
<div>
<div>{rendered.current} times rendered.</div>
<input
type={type}
name={name}
value={value}
placeholder={placeholder}
onChange={e =>
//pass state key and new value to onChange
onChange(stateKey, e.target.value)
}
/>
</div>
);
});
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Function I am importing and calling isn't running in react app

I am trying to run a firebase login function from a component in a react app. However, when I call it it isn't firing and I can't figure out why. I double and triple checked my spellings, casings and imports, and I still can't figure it out. Would someone mind taking a look? Thanks! The function I am trying to call is login user and I have commented where it is and also included the function.
//function to-be called
import axios from 'axios';
export const loginuser = (userData) => (dispatch) => {
console.log("loginuser hit"); //this is not firing and neither is my function.
axios
.post('/login', userData)
.then((res) => {
axios.defaults.headers.common['Authorization'] = res.data.token;
console.log(res.data.token);
})
.catch((err) => {
console.log("error in axios login");
});
};
/////class calling the function (in a different file)
import { loginuser } from './redux/userfunctions'
class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
};
}
handleSubmit = (event) => {
event.preventDefault();
console.log("submit selected");
const userData = {
email: this.state.email,
password: this.state.password
};
loginuser(userData); //this is where I call the function
};
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
});
};
render() {
return (
<div className = "sign-in">
<Topbar />
<form onSubmit={this.handleSubmit}>
<input
type = "text"
placeholder="Email"
name="email"
value={this.state.email}
onChange = {this.handleChange}
/>
<input
type = "password"
placeholder= "Password"
name="password"
value={this.state.password}
onChange = {this.handleChange}
/>
<button type = "submit"> Sign In </button>
</form>
</div>
);
}
}
Thanks again!
I can see 4 errors in your code
Your component needs to be wrapped by the HOC connect to use a dispatch action
You must return your axios call (as it is a Promise, the dispatcher will be able to wait before dispatching the result to the action)
You have to use the dispatch method as described in the doc
You have to pass the action to dispatch to the connect function and use it via this.props.myDispatchFunction (e.g this.props.loginuser)
//function to-be called
import axios from 'axios';
export const loginuser = (userData) => (dispatch) => {
console.log("loginuser hit"); //this is not firing and neither is my function.
// Added the return here
return axios
.post('/login', userData)
.then((res) => {
axios.defaults.headers.common['Authorization'] = res.data.token;
console.log(res.data.token);
})
.catch((err) => {
console.log("error in axios login");
});
};
/////class calling the function (in a different file)
import { connect } from 'react-redux' // Added the import of the connect function
import { loginuser } from './redux/userfunctions'
class Login extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
};
}
handleSubmit = (event) => {
event.preventDefault();
console.log("submit selected");
const userData = {
email: this.state.email,
password: this.state.password
};
this.props.loginuser(userData); // Use the this.props.loginuser instead of directly using the function to trigger the dispatcher
};
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
});
};
render() {
return (
<div className = "sign-in">
<Topbar />
<form onSubmit={this.handleSubmit}>
<input
type = "text"
placeholder="Email"
name="email"
value={this.state.email}
onChange = {this.handleChange}
/>
<input
type = "password"
placeholder= "Password"
name="password"
value={this.state.password}
onChange = {this.handleChange}
/>
<button type = "submit"> Sign In </button>
</form>
</div>
);
}
}
export default connect(null, { loginuser })(Login); // Wrapped the component with the `connect` function and added the dispatch function.
I've added some comments to your code + changed some stuff to fix it

Uncontrolled input React Hooks

I've started learing about react-hooks with a simple tutorial and to my surprise I got an error that I cannot figure out:
Warning: A component is changing an uncontrolled input of type text to
be controlled. Input elements should not switch from uncontrolled to
controlled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component.
After this error, my component disappears but I still can input data that will correctly print out in console.
I've tried setting initial state for inputs and changing
setInputs(inputs => ({
...inputs, [event.target.name]: event.target.value
}));
to
setInputs({...inputs, [event.target.name]: event.target.value});
but I'm still getting error.
JSX
import React from 'react';
import './styles/form.scss';
import useSignUpForm from './hooks/customHooks.jsx';
const Form = () => {
const {inputs, handleInputChange, handleSubmit} = useSignUpForm();
return (
<React.Fragment>
<div className="formWrapper">
<h1 className="header">Form</h1>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="nicknameInput">Nickname</label>
<input type="text" id="nicknameInput" name="nickname" onChange={handleInputChange}
value={inputs.nickname} required/>
<label htmlFor="emailInput">Email Address</label>
<input type="text" id="emailInput" name="email" onChange={handleInputChange}
value={inputs.email} required/>
<label htmlFor="lastName">Last Name</label>
<input type="text" id="lastName" name="lastName" onChange={handleInputChange}
value={inputs.lastName} required/>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</React.Fragment>
)
};
export default Form;
Hooks
import React, {useState} from 'react';
const useSignUpForm = (callback) => {
const [inputs, setInputs] = useState({});
console.log(inputs);
const handleSubmit = (event) => {
if (event) {
event.preventDefault();
}
};
const handleInputChange = (event) => {
event.persist();
setInputs(inputs => ({
...inputs, [event.target.name]: event.target.value
})
);
};
return {
handleSubmit,
handleInputChange,
inputs
};
};
export default useSignUpForm;
Any ideas what causes this error?
You are getting the error, because your inputs start their life as undefined and then have a value. If you replace this const [inputs, setInputs] = useState({}); with this:
const [inputs, setInputs] = useState({
nickname: '',
lastname: '',
email: ''
});
it will go away.
my favorite way of handling controlled inputs in react hooks is this syntax.. Make seperate state for each input you are trying to handle and then inside the onChange just call the setInput
onChange={e => setInput(e.target.value)}
the reason why you have an error is because the initial state is just an empty object, if you wanted to do it that way you would have to change your state to.
const [inputs, setInputs] = useState({
nickname: '',
lastname: '',
email: ''
});

Categories