React Noob - onChange Element Loses Focus - reactjs

I'm trying to create a basic login page that accepts user credentials and submits them to a login api. the problem is that when the onChange event fires, to set the user credentials, the element loses focus. Should I not be updating the credentials with onChange?
import React, {Component, PropTypes} from 'react';
import {Row, Col, FormControl, FormGroup, ControlLabel, HelpBlock, Checkbox, Button} from 'react-bootstrap';
export default class Login extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {credentials: {username: '', password: ''}};
this.onChange = this.onChange.bind(this);
this.onSave = this.onSave = this.onSave.bind(this);
}
onChange(event) {
const field = event.target.name;
const credentials = this.state.credentials;
credentials[field] = event.target.value;
return this.setState({credentials: credentials});
console.log(event);
}
onSave(event) {
event.preventDefault();
console.log(this.state.credentials);
this.props.actions.logInUser(this.state.credentials);
}
render() {
function FieldGroup({ id, label, help, ...props }) {
return (
<FormGroup controlId={id}>
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} />
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
);
}
const formInstance = (
<Col xs={12} md={8} mdOffset={2}>
<form>
<FieldGroup
name="username"
label="Username"
placeholder="Enter username"
value={this.state.credentials.username}
onChange={this.onChange}
/>
<FieldGroup
name="password"
label="Password"
type="password"
placeholder="Enter password"
value={this.state.credentials.password}
onChange={this.onChange}
/>
<Checkbox checked readOnly>
Checkbox
</Checkbox>
<Button type="submit" onClick={this.onSave}>
Submit
</Button>
</form>
</Col>
);
return formInstance;
}
}
Edit
Working when FieldGroup is moved to separate component, as suggested.
common/FieldGroup.jsx
import React from 'react';
import {FormControl, FormGroup, ControlLabel, HelpBlock} from 'react-bootstrap';
export default class FieldGroup extends React.Component {
constructor(props) {
super(props);
console.log('props');
console.log(props);
this.id = props.name;
this.label = props.label;
this.help = props.placeholder;
}
render() {
return (
<FormGroup controlId={this.props.name}>
<ControlLabel>{this.props.label}</ControlLabel>
<FormControl {...this.props} />
{this.props.help && <HelpBlock>{this.props.help}</HelpBlock>}
</FormGroup>
);
}
}
components/Login.jsx
import React, {Component, PropTypes} from 'react';
import {Col, Checkbox, Button} from 'react-bootstrap';
import FieldGroup from '../common/FieldGroup';
export default class Login extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {credentials: {username: '', password: ''}};
this.onChange = this.onChange.bind(this);
this.onSave = this.onSave = this.onSave.bind(this);
}
onChange(event) {
const field = event.target.name;
const credentials = this.state.credentials;
credentials[field] = event.target.value;
return this.setState({credentials: credentials});
}
onSave(event) {
event.preventDefault();
console.log(this.state.credentials);
this.props.actions.logInUser(this.state.credentials);
}
render() {
return (
<Col xs={12} md={8} mdOffset={2}>
<form>
<FieldGroup
name="username"
label="Username"
placeholder="Enter username"
value={this.state.credentials.username}
onChange={this.onChange}
/>
<FieldGroup
name="password"
label="Password"
type="password"
placeholder="Enter password"
value={this.state.credentials.password}
onChange={this.onChange}
/>
<Checkbox checked readOnly>
Checkbox
</Checkbox>
<Button type="submit" onClick={this.onSave}>
Submit
</Button>
</form>
</Col>
)
}
}

This is an interesting question, as it shows that even though stateless functional components (SFC's) don't have lifecycle methods, they do get unmounted and remounted when they change. This isn't documented all that clearly, but can be seen in the react implementation notes (in the case of SFC's, type is the SFC function itself).
Because FieldGroup is declared inside render, a new function is created on each rendering, which causes the previous SFC component in the virtual dom to unmount and be replaced by a new one. This will happen even when their renderings are exactly the same. As a consequence, any descendent input elements will unmount and remount, losing state and focus in the process.
If you simply call the SFC rather than instantiate it (i.e. {SFC({...sfcProps})} instead of <SFC {...sfcProps}/>, no virtual dom component is created for the SFC itself, and no remounting will occur if the rendering stays the same.
This snippet shows a local SFC, both instantiated and called as a function, as well as a regular top-level SFC. Only the bottom two inputs work correctly.
const SFC = ({rerender}) => <input onChange={rerender}/>
class App extends React.Component {
rerender = () => this.forceUpdate();
render () {
const LocalSFC = ({rerender}) => <input onChange={rerender}/>
return (
<div>
<div>Local SFC (loses state & focus on typing): <LocalSFC rerender={this.rerender}/></div>
<div>Local function application (works fine): {LocalSFC({rerender: this.rerender})}</div>
<div>Regular SFC (works fine): <SFC rerender={this.rerender}/></div>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
<div id="app"></div>
</body>
So to fix the problem, you could replace each <FieldGroup name=.. label=.. ../> by {FieldGroup({name: .., label:.., ..})}, but it makes more sense to do as Jaxx suggested and simply lift FieldGroup out of the Login class to the top-level or a separate file.

Returning inside an onChange function will cause it to lose an element's focus. Remove return from your onChange function.
You do not need to return setState.

Related

How can I filter special characters and spaces from my name input fields in react?

I wish to share this code just in case someone might need to solve such a problem of filtering unwanted characters when doing forms in react. For extras, my code shows how to pass props to components inside Route. For simplicity, I have focused on only these two inputs and omitted other stuff such as the submit button and css data for styling those classNames and ids.
import React, { Component } from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import SignupForm from "./components/SignupForm";
class App extends Component {
constructor() {
super();
this.state = {
firstName: "",
lastName: "",
};
//Binding this to the functions used as they wouldn't just work if not bound
this.changeFirstName = this.changeFirstName.bind(this);
this.changeLastName = this.changeLastName.bind(this);
this.lettersOnly = this.lettersOnly.bind(this);
}
changeFirstName(e) {
this.setState({ firstName: e.target.value });
}
changeLastName(e) {
this.setState({ lastName: e.target.value });
}
// Error handler functions
lettersOnly(nameInput) {//Replacing all characters except a-z by nothing with this function
let regex = /[^a-z]/gi;
nameInput.target.value = nameInput.target.value.replace(regex, "");
}
render() {
return (
<Router>
<div className="App">
<Route
exact
path="/"
comp={SignupForm}
render={() => (
<SignupForm
//SignupForm submit props
changeFirstNameHandler={this.changeFirstName}
firstNameValue={this.state.firstName}
changeLastNameHandler={this.changeLastName}
lastNameValue={this.state.lastName}
// Error handlers
nameCharacterFilter={this.lettersOnly}
/>
)}
/>
)}
/>
</div>
</Router>
);
}
}
export default App;
Below is the signup form, which is the child component in this aspect, and also a function component as opposed to its parent component:
import React from "react";
export default function SignupForm(props) {
return (
<div className="container" id="signupForm">
<h1>Signup Form</h1>
<div className="form-div">
<form>
<input
type="text"
placeholder="First Name"
onChange={props.changeFirstNameHandler}
value={props.firstNameValue}
onKeyUp={props.nameCharacterFilter}
className="form-control formgroup"
/>
<input
type="text"
placeholder="Last Name"
onChange={props.changeLastNameHandler}
value={props.lastNameValue}
onKeyUp={props.nameCharacterFilter}
className="form-control formgroup"
/>
</form>
</div>
</div>
);
}
NB: Welcome to improve this code, if you feel the need!
I think you can improve you're code with this changes:
Use the regex directly in the onChange event
Use only one method to update the values
Here is an example of what I mean: https://codesandbox.io/s/react-playground-forked-vreku?fontsize=14&hidenavigation=1&theme=dark
Regards!
Okay here is an improved code and much more cleaner. However, I have just omitted the React Router part to focus on functionality of the state and functions in this case.
I also want the user to see when they type an unwanted character that it actually typed but then just deleted on key up so I have created an independent function justLettersAndHyphen(nameField) from changeValue(event) that is triggered by onKeyUp.
import React from "react";
import SignupForm from "./SignupForm";
class App extends React.Component {
constructor() {
super();
this.state = {
firstName: "",
lastName: ""
};
this.changeValue = this.changeValue.bind(this);
this.justLettersAndHyphen = this.justLettersAndHyphen.bind(this);
}
changeValue(event) {
this.setState({
[event.target.name]: event.target.value,
});
}
// Error handler functions
justLettersAndHyphen(nameField) {
let regex = /[^a-z-]/gi;
nameField.target.value = nameField.target.value.replace(regex, "");
}
render() {
return (
<SignupForm
firstNameValue={this.state.firstName}
lastNameValue={this.state.lastName}
changeValueHandler={this.changeValue}
nameCharacterFilter={this.justLettersAndHyphen}
/>
);
}
}
export default App;
Child component edited with name property added.
import React from "react";
export default function SignupForm(props) {
return (
<div className="container" id="signupForm">
<h1>Signup Form</h1>
<div className="form-div">
<form>
<input
name="firstName"
type="text"
placeholder="First Name"
onChange={props.changeValueHandler}
onKeyUp={props.nameCharacterFilter}
value={props.firstNameValue}
/>
<input
name="lastName"
type="text"
placeholder="Last Name"
onChange={props.changeValueHandler}
onKeyUp={props.nameCharacterFilter}
value={props.lastNameValue}
/>
</form>
</div>
</div>
);
}

How can get Value from child component in react?

I create a InputComponent and I want to import this component in other component and do submit, but I can't do it. how can I get value from child component in my FormComponent
InputComponent.js
import React, {Component} from 'react';
class InputComponent extends Component {
constructor(props) {
super(props);
this.state = { currentVal: null }
}
render() {
return (
<div>
<input
value={this.state.currentVal}
onChange={(event) => this.setState({currentVal:event.target.value })}
/>
</div>
);
}
}
export default InputComponent;
FormComponent.js
import React,{Component} from 'react';
import InputComponent from '../../components/common/InputComponent';
class FormComponent extends Component {
constructor(props) {
super(props);
this.state = { }
}
CallSubmit= (event) => {
// how can get username and password ?
event.preventDefault();
console.log(event.target.vale)
}
render() {
return (
<div style={{width:100, height:500, marginTop:100}}>
<form onSubmit={ this.CallSubmit }>
<InputComponent name="username" />
<InputComponent name="password" />
<button>Send it</button>
</form>
</div>
);
}
}
export default FormComponent;
You can create an onChange action on child component, for example here is your FormComponent:
Create this function to handle change:
onChangeUsername(username){
this.setState({username})
}
Passing the props to child component:
<InputComponent name="username" onChange = {this.onChangeUsername} />
And in the child component(InputComponent), you can access this props and pass the data to it:
this.props.onChange(event.target.value)
move your onChange functions in parent component and store username and password in FormComponent component
In React, data flows one way, from parent to children.
Therefore, you need to have the input' actual states at the FormComponent and pass them as props to the InputComponent, and the function that deals with it at the FormComponent either.
<div>
<InputComponent
input={this.state.username}
onChange={this.handleInputChange}
/>
<InputComponent
input={this.state.password}
onChange={this.handleInputChange}
/>
</div>
A good article to understand it: https://openclassrooms.com/en/courses/4286486-build-web-apps-with-reactjs/4286721-understand-one-way-data-bindings

React form onSubmit is not working and input not allow to use white spaces

GitHub Issue: https://github.com/facebook/create-react-app/issues/4290#issuecomment-380521431
I found strength thing:
import React, { Component } from "react";
import FieldGroup from "./FieldGroup";
import { connect } from "react-redux";
import { Form, Button } from "react-bootstrap";
class UserUpdateForm extends Component {
constructor(props) {
super(props);
this.handleUserUpdate = this.handleUserUpdate.bind(this);
this.handleChangeName = this.handleChangeName.bind(this);
this.handleChangeNickname = this.handleChangeNickname.bind(this);
this.state = {
name: this.props.current_user.name,
nickname: this.props.current_user.nickname
};
}
handleChangeName(event) {
this.setState({
name: event.target.value
});
}
handleChangeNickname(event) {
this.setState({
nickname: event.target.value
});
}
handleUserUpdate(event) {
event.preventDefault();
const url = event.target.action;
console.log(url);
return false;
}
render() {
return (
<Form
onSubmit={this.handleUserUpdate}
action={this.props.action}
method="patch"
id={this.props.id}
>
<FieldGroup
role="form"
id={`${this.props.id}Name`}
type="text"
label="User Name"
placeholder="Enter User Name"
onChange={this.handleChangeName}
value={this.state.name}
/>
<FieldGroup
role="form"
id={`${this.props.id}Nickname`}
type="text"
label="User Nickname"
placeholder="Enter User Nickname"
onChange={this.handleChangeNickname}
value={this.state.nickname}
/>
<Button className="btn btn-primary btn-large centerButton" type="submit">Send</Button>
</Form>
);
}
}
export default connect(state => ({
current_user: state.current_user
}))(UserUpdateForm);
The Sign In/Sign Up form work, but UserUpdate not. Actually, onSubmit doesn't work. Also whitespaces skipped. I don't see console.log(url) or error.
I think, problem is in the <Navbar> Component of react-bootstrap, because, when i wrote my own <Modal> and inserted it to this <Navbar>, the problem wasn't solved. If I move <form> from <Navbar> all work.
App: https://on-money.herokuapp.com
Git: https://github.com/yashka713/on_money_front/blob/deploy/src/forms/UserUpdateForm.js
login: onioni#example.com
password: password
You should click on User profile and then click to FontAwesome Icon.
Thx :)
I found something, which looks like solution: https://github.com/react-bootstrap/react-bootstrap/issues/3105
And I want to cry.

React equivalent to ng-model

New to React, I have a TextField that I want to "bind" a value to it, I want it to change the value if the user enters value into the TextField and update the TextField if the value change through some API call.
Is there a way to do it ?
You can do this using state and onChange. Simple example included below:
<TextField
onChange={(name) => this.setState({name})}
value={this.state.name}
/>
A guide for updating TextInput based on a variable is located in the docs.
The way to do this in React is with state. Here's an example in JSX:
import React from 'react';
export default class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'default',
text2: 'default'
}
}
onChange(e) {
var obj[e.target.name] = e.target.value
this.setState(obj);
}
render() {
return (
<div>
<input type="text" name="text" value={this.state.text} onChange={this.onChange} />
<input type="text" name="text2" value={this.state.text2} onChange={this.onChange} />
</div>
);
}
}
The same thing with React hook
import React,{useState} from 'react';
export default MyForm = () => {
const [text, setText] = useState('default')
const [text2, setText2] = useState('default')
return (
<div>
<input type="text" name="text" value={text} onChange={(e)=> setText(e.target.value)} />
<input type="text" name="text2" value={text2} onChange={(e)=> setText2(e.target.value)} />
</div>
)
}
Two ways data binding in angular work out of the box by using ngModal directive
NgModal: Creates a FormControl instance from a domain model and binds it to a form control element
import {Component} from '#angular/core';
#Component({
selector: 'example-app',
template: `
<input [(ngModel)]="name" #ctrl="ngModel" required>
<p>Value: {{ name }}</p>
<p>Valid: {{ ctrl.valid }}</p>
<button (click)="setValue()">Set value</button>
`,
})
export class SimpleNgModelComp {
name: string = '';
setValue() { this.name = 'Nancy'; }
}
In ReactJS, we don't have such a built-in option to handle two ways data binding, you need to use the state and add an onChange event to the input.
React Controlled Input allow us to implement data binding in React: we bind a value to a state variable and an onChange event to change the state as the input value changes.
Refer to the below snippet:
class App extends React.Component {
constructor() {
super();
this.state = {name : ''}
}
handleChange = (e) =>{
this.setState({name: e.target.value});
}
render() {
return (
<div>
<input type="text" value={this.state.name} onChange={this.handleChange}/>
<div>{this.state.name}</div>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
This is pretty straightforward using useState React Hook and TextInput
import { useState } from "react";
import { TextInput, Text, View } from "react-native";
const App = () => {
const [text, setText] = useState("hello");
return (
<View>
<TextInput value={text} onChangeText={setText} />
<Text>Text value: {text}</Text>
</View>
);
};
export default App;
See it working here

this.state null in reactjs component

i am just trying first something simple like only printing the this.state.validForm in the handleSubmit function, but i cant seem to access this.state.validForm. First i tried directly then with this function, but to no avail. I am new to react.
import React, { Component } from 'react';
import TextInput from './TextInput';
class RequestForm extends Component {
constructor(props) {
super(props);
this.state = {validForm : "false"};
this.getInfoForm = this.getInfoForm.bind(this);
}
getInfoForm() {
return this.state.validForm;
}
handleSubmit(event) {
event.preventDefault();
console.log('submit values are');
console.log(event.target.src.value);
console.log(event.target.email.value);
console.log(this.state.validForm);
console.log(this.getInfoForm());
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<TextInput
uniqueName="email"
name="email"
text="Email Address"
required={true}
minCharacters={6}
validate="email"
errorMessage="Email is invalid"
emptyMessage="Email is required"
/>
<TextInput
text="User"
name="User src"
required={true}
minCharacters={3}
validate="notEmpty"
errorMessage="Name is invalid"
emptyMessage="Name is required"
/>
<button type="submit">Submit</button>
</form>
);
}
}
export default RequestForm;
The issue is with your render method. Your onSubmit event has its own context so when you do this.handleSubmit, you're going to be passing the wrong context to handleSubmit. Simply bind this and you'll be set!
<form onSubmit={this.handleSubmit.bind(this)}>
Or with the proposed bind operator:
<form onSubmit={::this.handleSubmit}>

Resources