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}>
Related
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>
);
}
I'm creating a ReactJS package. I've created a component called Form which contains a normal form. When calling it on the App.js I add components on it which called TextField. So, the code on App.js is:
import React, { Component } from 'react'
import { Form, TextField } from 'form2'
import './App.css'
export default class App extends Component {
render () {
return (
<div>
<Form>
<TextField placeholder="Type your name..." />
<TextField placeholder="Type your email..." />
</Form>
</div>
)
}
}
And the code of the Form component is:
import React, { Component } from 'react'
class Form extends Component {
constructor(props) {
super(props)
this.state = {
values: {}
}
}
renderChildren = () => {
return this.props.children;
};
render() {
return <form>
{ this.renderChildren() }
</form>
}
}
export default Form
And this is mt TextField component:
import React, { Component } from 'react'
class TextField extends Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
onChange = ({ target }) => {
this.setState({ value: target.value });
};
render() {
return <input onChange={this.onChange} {...this.props}>
{this.state.value}
</input>
}
}
export default TextField;
The problem is happening when I type something on the input, the inputs disappeared and got this error message on the console:
The above error occurred in the <input> component
and this one
Uncaught Invariant Violation: input is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.
Change:
render() {
return <input onChange={this.onChange} {...this.props}>
{this.state.value}
</input>
}
To:
render() {
return <input defaultValue={this.state.value} onChange={this.onChange} {...this.props} />
}
Or (depending on the behavior your want):
render() {
return <input value={this.state.value} onChange={this.onChange} {...this.props} />
}
The existing code is trying to set the child of input to {this.state.value}.
I tried to create a custom input component with inputRef (material ui Input component). Looks like the component reference is working but I'm unable to enter any value in the text field after i set the value attribute. I think it's because of the way i implemented the onchange event. I'm not sure what am i missing. Please help.
Here is the codesandbox url
https://codesandbox.io/s/pjlwqvwrvm
Actually you don't need a onChange prop to for get the changed value..
Just get the value from onchange and set the value in state value.
Another mistake is you are not created the constructor, and gave this.props.value to the value prop. That's it not get updated..
Now I created the constructor and give the this.state.value to the value props.
Now you get your onchanged value in custominput component and your submit function also..
import React from "react";
import { render } from "react-dom";
import { Input } from "material-ui-next";
import trimStart from "lodash/trimStart";
import PropTypes from "prop-types";
const defaultProps = {
state: "",
onChange: () => {} // no need
};
const propTypes = {
state: PropTypes.string,
onChange: PropTypes.func
};
class App extends React.Component {
constructor() {
super();
this.state = {
value:''
}
}
handleSubmit(event) {
event.preventDefault();
console.log("state: " + this.state.value); //shows onChanged value in console
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
<CustomInput
labelText="State"
id="state"
value={this.state.value}
onChange={e=> {
this.setState({value:e.target.value})
}}
/>
</form>
</div>
);
}
}
App.propTypes = propTypes;
App.defaultProps = defaultProps;
class CustomInput extends React.Component {
render() {
const {
classes,
formControlProps,
value,
onChange,
labelText,
id,
labelProps,
inputRef,
inputProps
} = this.props;
return (
<div {...formControlProps}>
{labelText !== undefined ? (
<div htmlFor={id} {...labelProps}>
{labelText}
</div>
) : null}
<Input
classes={{
root: labelText !== undefined ? "" : classes.marginTop
}}
id={id}
value={value} ///////// Fixed ////////
onChange={onChange}
inputRef={inputRef}
{...inputProps}
/>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Here is code in sandbox check it..
https://codesandbox.io/s/84rjk4m8l8
You can either go on inputRef - then your value and onChange event are extra - it is called uncontrolled Component. You can see more about it here: https://reactjs.org/docs/uncontrolled-components.html
Or you can do it with value & onChange event - and work with controlled components, you can find more about controlled components here: https://reactjs.org/docs/forms.html#controlled-components
How to solve it (uncontrollable) with inputRef:
class App extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
console.log("input value: ", this.input.value); // will now show you correct input value
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
labelText="State"
id="state"
inputRef={input => {
this.input = input;
}}
/>
<Button onClick={this.handleSubmit} color='primary'>Submit</Button>
</form>
</div>
);
}
}
Instead of "this.state = input", bind input to something else, because this.state is reserved for local state of React Component, and it won't work with it, not like that.
How to solve it (controllable) with state, value & onChange event:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.state || ''
}
}
handleSubmit = (event) =>{
event.preventDefault();
console.log("state: ", this.state.value); // will now show you correct input value
}
handleChange = (event) => {
this.setState({value: event.target.value});
}
render() {
const {value} = this.state;
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
labelText="State"
id="state"
onChange={this.handleChange}
value={value}
/>
<Button onClick={this.handleSubmit} color='primary'>Submit</Button>
</form>
</div>
);
}
}
Note that I added the constructor and defined the local state for component, and I'm changing value inside of state with this.setState (because state is immutable, and that's the right way to update it).
In both examples, you are able to get input value inside of handleSubmit method, will you work with controllable or uncontrollable components, it's up to you :)
Use value={this.state} or value={this.value}
Here is Your sandbox code Updated
https://codesandbox.io/s/qqk2qoxmlj
In my case, I was mistakenly using state from parent component in child component, since I had declared child component inside parent itself. Moving whole state to child component solved the problem.
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.
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