What is best way to create forms in react? - javascript

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>

Related

React Hook not saving key value pairs in object, MongoDB just saving id & __v

Using: MongoDB, Express, ReactJS, Node.js to save an object containing student's first name, last name, and grade.
I expect it to save like this:
{
firstName: "Jaron",
lastName: "Smith",
grade: "2",
_id: "60f205222e2569cc122c5417",
__v: 0
},
Current result: not even saving key / value pair, just the assigned values by MongoDB:
{
_id: "60f205222e2569cc122c5417",
__v: 0
},
My createStudent.js file with React Hook:
import React, { useState } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import axios from 'axios'; //Axios is designed to handle http requests and responses. It's used more often than Fetch because it has a larger set of features and it supports older browsers.
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(1),
width: '25ch',
},
},
}));
export default function Create() {
const classes = useStyles();
const [student, setStudent] = useState({ //creating a pice of state and initializing it with an object
firstName: '',
lastName: '',
grade: ''
})
const updateStudent = e => {
e.preventDefault();
setStudent({
...student,
[e.target.name]: e.target.value
});
}
const createStudent = () => {
console.log(JSON.stringify(student));
axios.post('http://localhost:5000/students', student) //sends data from client (useState) to backend
.then(
console.log("result after post:" + JSON.stringify(result))
window.location.reload()
).catch(error => {
console.log(error)
})
}
return (
<>
<h2>Create Student</h2>
<form className={classes.root} noValidate autoComplete="off">
<TextField
id="outlined-basic"
name="firstName"
label="First Name"
variant="outlined"
value={student.firstName}
onChange={updateStudent}
/>
<TextField
id="outlined-basic"
name="lastName"
label="Last Name"
variant="outlined"
value={student.lastName}
onChange={updateStudent}
/>
<TextField
id="outlined-basic"
name="grade"
label="Grade"
variant="outlined"
value={student.grade}
onChange={updateStudent}
/>
<Button variant="contained" color="primary" onClick={createStudent}>
Create
</Button>
</form>
</ >
);
}
I believe that it's the syntax of the React Hook. If not, my server is here:
server side repo
I've tried refactoring 'update student' without luck. I console.log() e.target and it's recognizing the text entered in the form.
On the line with 'axios.post' in hard coded data to upload in place of 'student' on that same line and it still created an object like the current result above.
I know it's got to be something simple! Any feedback is welcomed and appreciated!
I think your problem is here :
const updateStudent = e => {
setStudent({
...student,
[e.target.name]: e.target.value
});
e.preventDefault(); // this line
}
you should set your state first before do e.preventDefault();
Your are calling e.preventDefault() in you updateStudent function but you don't need it, you have to use it on the createStudent function.
const updateStudent = e => {
e.preventDefault(); // delete this line
setStudent({
...student,
[e.target.name]: e.target.value
});
}
const createStudent = () => {
e.preventDefault();
...
}

react js form inputs using eval?

I have a form with 58 inputs so, i wanted to do this dinamically, having this fields with the values i need(name, placeholder, value)...I've mapped throw an array of strings and got name and placeholder ok BUT when I tried to obtain the value I've read a bit and found the eval function...and in deed it solved my problem...but then react js sent me an 'eval can be harmful' warning so...
Question 1: In my case is really eval harmfull?How can I avoid that in my way of being a by the book programmer?
Question2: In which other way can I obtain my value's value?
now I show a piece of my code...:
{variables.map((variable, index) =>
<div className="form-group">
<input
key={index}
type="text"
name={variable}
className="form-control"
placeholder={variable}
onChange={handleInputchange}
value={eval(variable)}
/>
</div>
)}
where variables is an array of strings
thanks for your time
It would be better to have an object if possible for the fields, that way you can access the values by key like this:
import { useState } from 'react';
const Test = () => {
const [formData, setFields] = useState({
field1: '',
field2: '',
field3: '',
});
const handleChange = e => {
e.preventDefault();
setFields(prev => ({ ...prev, [e.target.name]: e.taget.value }));
};
return (
<div>
{Object.keys(formData).map(fieldName => (
<div className="form-group" key={fieldName}>
<input
type="text"
className="form-control"
name={fieldName}
placeholder={fieldName}
onChange={handleChange}
value={formData[fieldName]}
/>
</div>
))}
</div>
);
};
export default Test;
If you must have the initial fields as an Array i'd probably just use reduce to turn the initial form data into an object like this:
import { useState } from 'react';
const fields = ['field 1', 'field 2', 'field 3'];
const initialState = fields.reduce((acc, field) => ({ ...acc, [field]: field }), {});
const Test = () => {
const [formData, setFormData] = useState(initialState);
const handleChange = e => {
e.preventDefault();
setFormData(prev => ({ ...prev, [e.target.name]: e.taget.value }));
};
return (
<div>
{Object.keys(formData).map(fieldName => (
<div className="form-group" key={fieldName}>
<input
type="text"
className="form-control"
name={fieldName}
placeholder={fieldName}
onChange={handleChange}
value={fields[fieldName]}
/>
</div>
))}
</div>
);
};
export default Test;

React with Fluent UI Form onChange not working (very simple code)

I am using React with FluentUI to build a simple form, code is following
import React, { useState, FormEvent } from "react";
import { PrimaryButton } from "office-ui-fabric-react/lib/Button";
import { IUserFormValues } from "../../models/user";
import { Stack, TextField } from "#fluentui/react";
const NewUIForm = () => {
const [user, setUser] = useState<IUserFormValues>({
email: "",
password: "",
});
const handleInputChange = (
event: FormEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
const { name, value } = event.currentTarget;
setUser({ ...user, [name]: value });
};
const log123 = () => {
console.log(user.email);
};
return (
<Stack>
<Stack>
<Stack.Item grow>
<TextField
placeholder="UserName"
label="User Name"
value={user.email}
name="email"
onChange={handleInputChange}
/>
<TextField
placeholder="Password"
label="Password"
value={user.password}
name="password"
onChange={handleInputChange}
/>
</Stack.Item>
<PrimaryButton onClick={log123}>Add</PrimaryButton>
</Stack>
</Stack>
);
};
export default NewUIForm;
every time when I type something in the TextField I will get this error
TypeError: Cannot destructure property 'name' of 'event.currentTarget' as it is null.
Can someone help me? Thanks!!
Fluent UI onChange function expects two parameters: event and value(optional)
(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => void
https://developer.microsoft.com/en-us/fluentui#/controls/web/textfield#implementation
You may change your handleInputChange function like this:
const handleInputChange = (
event: { target: HTMLInputElement },
newValue:String
):void => {
const { name } = event.target;
setUser({ ...user, [name]: newValue });
};
You can check this working fiddle from here

React hooks not keeping data

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();
...
}

React Formik : how to use custom onChange and onBlur

I'm starting out with the formik library for react, and I can't figure out the usage of the props handleChange and handleBlur.
According to the docs, handleBlur can be set as a prop on a <Formik/>, and then has to be passed manually down to the <input/>.
I've tried that, with no success :
(I'm keeping the code about handleBlur for more clarity)
import React from "react";
import { Formik, Field, Form } from "formik";
import { indexBy, map, compose } from "ramda";
import { withReducer } from "recompose";
const MyInput = ({ field, form, handleBlur, ...rest }) =>
<div>
<input {...field} onBlur={handleBlur} {...rest} />
{form.errors[field.name] &&
form.touched[field.name] &&
<div>
{form.errors[field.name]}
</div>}
</div>;
const indexById = indexBy(o => o.id);
const mapToEmpty = map(() => "");
const EmailsForm = ({ fieldsList }) =>
<Formik
initialValues={compose(mapToEmpty, indexById)(fieldsList)}
validate={values => {
// console.log("validate", { values });
const errors = { values };
return errors;
}}
onSubmit={values => {
console.log("onSubmit", { values });
}}
handleBlur={e => console.log("bluuuuurr", { e })}
render={({ isSubmitting, handleBlur }) =>
<Form>
<Field
component={MyInput}
name="email"
type="email"
handleBlur={handleBlur}
/>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
</Form>}
/>;
What is wrong with this approach ?
How are handleBlur and handleChange actually supposed to be used ?
You'll need to remove the first handleBlur from Formik as blur event is only valid on the field level and do something like the following in your Field element:
<Field
component={MyInput}
name="email"
type="email"
onBlur={e => {
// call the built-in handleBur
handleBlur(e)
// and do something about e
let someValue = e.currentTarget.value
...
}}
/>
See https://github.com/jaredpalmer/formik/issues/157
According to the above mention question code, you just need to change only one thing i.e. onBlurCapture={handleBlur}
which works on html input element. onBlur, onFocusOut are not supported by the react. However, I got the same issue and finally resolved with component in react-formik with render props.
i faced the same problem using onChange method, which i think do not exist in formik props.
so i used the onSubmit method as it's available in the formik props which gives us the fields values and then passed that values to the concern function like so ...
<Formik
initialValues={initialValues}
validationSchema={signInSchema}
onSubmit={(values) => {
registerWithApp(values);
console.log(values);
}}
>
and there you can use, i simply updated the state and passed it to axios like so...
const [user, setUser] = useState({
name: "",
email: "",
password: ""
});
const registerWithApp = (data) => {
const { name, email, password } = data;
setUser({
name:name,
email:email,
password:password
})
if (name && email && password) {
axios.post("http://localhost:5000/signup", user)
.then(res => console.log(res.data))
}
else {
alert("invalid input")
};
}
and its working ...
I hope its helps you.

Categories