How access specific DOM in React js - reactjs

I'm trying to get input tags' HTML inner values when submitting a form.
private handleSubmit = (event: any) => {
event.preventDefault();
console.log(event.currentTarget);
};
When the submit the form, it calls the function handleSubmit and it console logs the following.
Under the form tag, the first div has username value and the second div has password value. I would like to acess the two values. I think I should use DOM to do that, but can't be sure if I'm going for the right direction cuz I found some postings saying using DOM is not recommended.
Can anyone explain how I can acheive this?

Ideally you should update your state as the user enters information, and then access the data from the state. This would also allow you to run any validation on the data prior to it going into the state if you'd like.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
username: null,
password: null
}
this.submitForm = this.submitForm.bind(this);
this.updateState = this.updateState.bind(this);
}
updateState (e) {
this.setState({[e.target.name]: e.target.value})
}
submitForm (e) {
e.preventDefault();
console.log(this.state);
}
render() {
return (
<div className="App">
<form onSubmit={this.submitForm}>
<input type="text" name="username" placeholder="username" onChange={this.updateState} /><br />
<input type="password" name="password" placeholder="password" onChange={this.updateState} /><br />
<button type="submit">Submit</button>
</form>
</div>
);
}
}
export default App;
The above code does the following:
Stores default values for username and password. While this isn't required, it makes the code more readable
binds this to functions that need to access state
Uses an updateState() function that is called onChange of the inputs
updateState uses the name attribute of the input as the key for the state
You could customize the updateState() function to do some validation, before saving to state if you'd like.
Excessive Rendering
ReactJS is pretty smart to no re-render the REAL DOM if your render() method doesn't actually rely on the state values that were updated; however, if you'd like to prevent ReactJS from even creating the Virtual DOM and comparing, you could utilize the shouldComponentUpdate() lifecycle hook.
In the particular example above, since render doesn't rely on ANYTHING in state, you could simply add the following method:
shouldComponentUpdate(prevState, nextState) {
return false;
}
That will prevent the render method from EVER re-rendering, which is most likely not going to work in a normal component, thus you could do something like this instead, only re-rendering on values you care about.
shouldComponentUpdate(prevState, nextState) {
if (nextState.email !== prevState.email) {
return true
}
return false;
}
Demo
https://repl.it/#AnonymousSB/SO53689072

If you want to use the DOM, once you have the form element (event.currentTarget in your case), you can use the .elements property to access a list of child inputs and buttons.
Alternatively, you can use React refs to keep track of the underlying HTML element when it's rendered.
render() {
return ... <input ref={(e) => this._name = e; } ....> ... ;
}
handleSubmit(e) {
var name = this._name ? this._name.value : '';
....
}

This can achieve what you want to
class Login extends Component{
state={
username:"",
password:""
}
onChange = (event)=>{
event.preventDefault()
this.setState({
[event.target.name]: event.target.value})
}
onSubmit = (event)=>{
event.preventDefault()
// submit whatever is in state from here
console.log(this.state)
}
render(){
return(<div>
<form onSubmit={handleSubmit}>
<input type="text" name="username" onChange={this.onChange} /><br />
<input type="password" name="password" onChange={this.onChange} /><br />
<button type="submit">Submit</button>
</form>
</div>)
}
}

Related

How to validate the form and send user to another page on successful data filled in form fields in React JS?

I want to send a user to Successful component of ReactJS after form with user and password has been filled up.
Here is the code:
import React from 'react';
import Successful from './Successful';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: '',
pass: ''
}
}
onsubmit = () => {
<Successful/>
}
render() {
return (
<form onSubmit={this.onsubmit}>
<div className="App">
<input type="text"
placeholder="Enter user"
validate="required"
onChange={() => this.state.user}
/>
<input type="password"
placeholder="Enter password"
validate="required"
onChange={() => this.state.password}
/>
<button type="submit">Submit</button>
</div>
</form>
)
}
}
export default App;
What I undestand you want to do is validate the password to see if it matches a user's password, if that happends, then redirect the user to another page? In that case, what you are doing good. Your validation logic should be located inside your onsubmit method, which triggers automatically when the user submits the form.
Inside that method you first, you should catch your form submit event, to prevent your page from reloading (which is the default behaviour when submitting a form). To do this, you receive the event object (which is passed automatically), and call it's preventDefault method:
onsubmit = (event) => {
event.preventDefault() // To prevent page reloading...
}
Here you check wheter the password entered is correct with a simple conditional, and then, there is the redirection part. I see you are just returning the component you wanna render (<Successful />). If what you wanna do is just show this component on the same page, you should add a new state property, that controls wheter the form input was successful or not, and then show or hide that component based on that state:
// Add the success prop
this.state = {
user: "",
pass: "",
success: false
}
onsubmit = (event) => {
event.preventDefault()
// Check if your password is valid...
// If its valid, then:
this.state.success = true
}
render() {
return (
<div>
... your other stuff here ...
{this.state.successful && <Successful />}
</div>
)
}
What {this.state.successful && <Successful />} does is that it only renders the component after &&, if the condition is true. Because of your validation process, this.state.successful is only true if the entered password passed the validation process, so your success component gets rendered only if the condition is fulfilled. The brackets in {this.state.... && <Successful />} are required.
If what you want to do is really redirect your user to another page, you should use something like react-router-dom (a library that allows you to do redirecting) and instead return its <Redirect /> component or browserHistory method. Its really too long to explain here, but you can check this or look for some tutorial on react-router because is an extensive library.
import React from 'react';
import Successful from './Successful';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: '',
pass: '',
success: false
}
}
onsubmit = () => {
this.state.setState({success:true})
}
const form = <form onSubmit={this.onsubmit}>
<div className="App">
<input type="text"
placeholder="Enter user"
validate="required"
onChange={() => this.state.user}
/>
<input type="password"
placeholder="Enter password"
validate="required"
onChange={() => this.state.password}
/>
<button type="submit">Submit</button>
</div>
</form>
render() {
return (
{!this.state.success ? form :
<Successful/>}
}
export default App;
Something like this could work. Here I added a "success" variable to the state. Initally set as false so the "form" is return and rendered.
onSubmit we set the state to true, this triggers a re-render and then we return the componant instead.
(I think I set the state correctly, I usually use hooks these days)
import { useHistory } from "react-router-dom"; //this goes on top
const history = useHistory();
onsubmit = () => {
//your logic for user input validation goes here
if(input is validated) {
history.push("/success");
}
else {
//show error here
}
}
Since you have added react-router in the tags. I am assuming that you have a route with success componenent.

React.js, improve generating object from form inputs

In vanilla JS I could just use formData for this, and make an Object.fromEntries() from it.
In React I wasn't sure how to do this right, so here's what I came up with after some googling around:
class App extends Component{
constructor(props){
super(props);
this.autoData = {
title:'',
description:'',
year:'',
color:'',
status:'',
price:''
}
}
handleAutoData = e => {
if (e.target.id in this.autoData) {
this.autoData[e.target.id] = e.target.value
}
}
handleAutoForm = e => {
e.preventDefault()
if (Object.keys(this.autoData).every(k => this.autoData[k])) {
this.props.addAuto(this.autoData)
}
}
render(){
...
return (
<div className="App">
<form className="form" onSubmit={this.handleAutoForm}>
<input type="text" className="form_text" placeholder="Name" id="title" onChange={this.handleAutoData} />
<input type="text" className="form_text" placeholder="Year" id="year" onChange={this.handleAutoData} />
<input type="text" className="form_text" placeholder="Price" id="price" onChange={this.handleAutoData} />
<input type="text" className="form_text" placeholder="Details" id="description" onChange={this.handleAutoData} />
<button type="submit"> Send > </button>
</form>
</div>
)
}
}
This does the job and handleAutoForm pushes the object into Redux store. However:
Is there a better alternative for onChange event? I know it's the go-to way of handling form inputs, but right now it's spamming/overwriting my values on every keystroke. I'd only like to push a value once I stop typing/field loses focus. Out of alternatives, I saw articles of onFocusOut, but it's not supported or has issues.
Right now I'm mutating the component's state directly. Not sure if it's critical, since I'll be pushing the state to Redux anyway. I wanted a local object inside handleAutoData, just so I could write the values into it, but every time an onChange is called, a new object is made and it overwrites the previous values. Problem is, I can't use setState because of e.target's nature - it keeps complaining about missing brackets on render, because of all the dots (when I do something like this.setState({autoData[e.target.id]:e.target.value}) ). And if I assign it to a temporary variable (like let autoKey = e.target.id), setState pushes the autoKey as key, instead of e.target.id. What could be done with this?
Usual way is to use name or id along with onChange event.
state = {
title:'',
description:'',
year:'',
color:'',
status:'',
price:''
}
handleChange = (e) => {
this.setState({
[e.target.name]:e.target.value
});
handleSubmit = (e)=>{
e.preventDefault();
//Push state to redux,make API
call etc
}

Difference between React controlled component and uncontrolled component

I am new to react and while learning I come across this example of controlled component.
function App() {
let [fName, setFName]=useState('');
return (
<div className="container">
<h1>Hello {fName }</h1>
<input name ='fname' value={fName} onChange={(e)=> setFName(e.target.value)} type="text" placeholder="What's your first name?" />
</div>
);
}
just adding value={fName} makes is controlled . I don't actually understand what does it mean by controlled component and uncontrolled. Can you explain it from beginner prospective.
An uncontrolled component means that you will let the component itself manage the value. It's own internal mechanism will keep track of it.
Now when you add the value property to the input component, you will start to "control" the component yourself. The value you put into that property, will be the value that will be displayed.
You can literally control the value yourself, by just passing it in as is, or by changing the value before passing it in.
Controlled Components
These components have what is called a callback function that is triggered each time we enter something new in the form element.
Triggering the function will typically store or update what we type in the same React component that displays the form element that was used
Most widespread use it with forms
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Unontrolled Components
These components such as <input> typically maintain their own state and update it based on user input.
In other words, they will accept what we type and have the responsibility of remembering it, and in order to retrieve the values they remembered, you have to get it when you need it.
The latter usually happens during form submission. They can be classified under uncontrolled components.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Here, Reactjs documentation provided explanation.
A Controlled Component is one that takes its current value through props and notifies changes through callbacks like onChange. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a dumb component/stateless component.
An Uncontrolled Component is one that stores its own state internally, and you query the DOM using a ref to find its current value when you need it. This is a bit more like traditional HTML.
React form components support both controlled and uncontrolled usage:
// Uncontrolled:
<input type="text" defaultValue="hey" ref={inputRef} />
// Controlled:
<input type="text" value={this.state.value} onChange={onHandleChange} />

Able to type inside input text field without onChange method

I am learning React and below one is sample code I am trying out. I am able to render this component and able to type in characters in input field without any handleChange() method ? Is this fine ? because what I know is, in order to make input fields available for typing, we need to add handleChange method something like below
handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
import React from "react";
class StudentForm extends React.Component {
constructor() {
super();
}
render() {
return (
<div>
<form>
<h1>Student Form</h1>
<input type="text" name="firstname"></input>
</form>
</div>
);
}
}
export default StudentForm;
handleChange is for setting the state value.
without onChange handler you can type in but your value is not getting stored anywhere.
For example, if you try to access your state this.state.firstname you will always get undefined.
You should have controlled component. Which is a simple and cleaner way access and store value in state.
To make your component controlled, you should have value and onChange props on input,
<input type="text" name="firstname" value={this.state.firstname} onChange={this.handleChange.bind(this)}></input>
Yes, consider the following
<input type="text" name="firstname" />
This is an uncrontrolled input which means React doesn't now about it's value nor how to change it. To make an input controlled you need to explicitly specify the value and onChange properties to bind this input to React's state
const Input = () =>{
const [value, setValue] = useState('')
return <input value={value} onChange={e => setValue(e.target.value)} />
}
Now the input is fully controlled by React, which provides the value it must print and a way to change it
After making below changes, I made this input element as controlled element and now I am not able to type in anything without using onChange handler.
import React from "react";
class StudentForm extends React.Component {
constructor() {
super();
this.state = {
firstname: ""
};
}
render() {
return (
<div>
<form>
<h1>Student Form</h1>
<input
type="text"
name="firstname"
value={this.state.firstname}
></input>
</form>
</div>
);
}
}
export default StudentForm;

ReactJS unable to edit the textbox created through state variable

I have the following code where the issue is the amount1 which the textbox is created through state is unable to edit. When i edit this textbox the value 10 is not changing from the textbox. The other normal textbox works as usual. Anyone knows the issue?
constructor(props) {
super(props);
this.state = {
amount1:"10",
amount2:"20",
input:"",
}
}
componentDidMount(){
var options = <input type="text" name="amount1" value={this.state.amount1}>
this.setState({'input':options})
}
render() {
return(
<div>
{this.state.input}
<input type="text" name="amount2" value={this.state.amount2}>
</div>
)
}
You are missing the close tag of input and since you are passing value from the state the input becomes read-only. So try defaultValue instead of value to make it editable.
Both your inputs don't change their value since they are bound to the values in the state,
this is your code
What you need to do is add an event handler to your input tag that changes the value saved in the state.
handleAmountInput = (event) => { this.setState({ amount: event.target.value }) }
render() {
return( this.handleAmountInput(event)} value={this.state.amount}> )
}
And you should close your input tag <input />
I think if you changed your render to the following, along w/ the onChange binding event, you should get the appropriate values in state.
onChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
render() {
return (
<div>
<input
type="text"
name="amount1"
value={this.state.amount1}
onChange={this.onChange.bind(this)}
/>
<input
type="text"
name="amount2"
value={this.state.amount2}
onChange={this.onChange.bind(this)}
/>
</div>
);
}
As mentioned previously, you are creating readOnly, uncontrolled elements. This is a good article on controlled (react recommended) vs uncontrolled:
https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/

Resources