I have the following CustomInput component:
import React from 'react';
const CustomInput = props => (
<div className="form-group">
<label className="form-label">{props.title}</label>
<input
className="form-input"
name={props.name}
type={props.inputType}
value={props.content}
onChange={props.controlFunc}
placeholder={props.placeholder}
/>
</div>
);
CustomInput.propTypes = {
inputType: React.PropTypes.oneOf(['text', 'number']).isRequired,
title: React.PropTypes.string.isRequired,
name: React.PropTypes.string.isRequired,
controlFunc: React.PropTypes.func.isRequired,
content: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
]).isRequired,
placeholder: React.PropTypes.string,
};
export default CustomInput;
And this is my form:
import React, { PropTypes } from 'react';
import { Field, reduxForm } from 'redux-form';
import CustomInput from '../components/CustomInput';
const renderMyStrangeInput = field => (
<CustomInput
inputType={'number'}
title={'How many items do you currently own?'}
name={'currentItems'}
controlFunc={param => field.input.onChange(param.val)}
content={{ val: field.input.value }}
placeholder={'Number of items'}
/>
);
class ItemsForm extends React.Component {
constructor(props) {
super(props);
}
render() {
const { handleSubmit } = this.props;
return (
<form className="container" onSubmit={handleSubmit}>
<h1>Nuevo Pedido</h1>
<Field name="myField" component={renderMyStrangeInput} />
<div className="form-group">
<button type="submit" className="btn btn-primary input-group-btn">Submit</button>
</div>
</form>
);
}
}
ItemsForm.propTypes = {
};
ItemsForm = reduxForm({
form: 'Items',
})(ItemsForm);
export default ItemsForm;
I can render my component, but there are some issues with the content type. First, if I set the CustomInput to accepts numbers I get:
he specified value "[object Object]" is not a valid number. The value must match to the following regular expression: -?(\d+|\d+\.\d+|\.\d+)([eE][-+]?\d+)?
Second, if I set the inputType to text, it renders a:
[object Object]
so, the console is giving the following warning:
Warning: Failed prop type: Invalid prop `content` supplied to `CustomInput`.
How can I set the content properly?
I think the issue is that you are trying pass strings as objects
<CustomInput
inputType="number"
title="How many items do you currently own?"
name="currentItems"
controlFunc={param => field.input.onChange(param.val)}
content={field.input.value}
placeholder="Number of items"
/>
You are passing object via props and you must pass string or number.
content={{ val: field.input.value }} // no!
content={field.input.value} // yes! :)
Related
I'm new to React. I'm trying to do simple validation of a form elements. I'm stuck at validating input data and setting the 'name_error' state as the error msg inside <p> tag. Leaving my code below
// App.js
import React, { Component } from 'react';
import ValidateName from './component/validation';
import Modal from './modal';
class Home extends Component {
state = {
show: false,
name: "",
name_error: ""
}
handleChanges = (e) => {
const name = e.target.name;
const value = e.target.value;
this.setState({[name] : value})
}
render() {
console.log(this)
return (
<div>
<div className='Main'>
{/* < Main /> */}
<button id='add' onClick={()=>{this.setState({show: true})}}>Add</button>
</div>
{this.state.show &&
<Modal>
<form>
<div className='modalContainer'>
<b className='title'>Register</b>
<button id='xmark' onClick={()=>{this.setState({show: false})}} >×</button>
<label for='name' >Name</label><br />
<input type="text" id='name' placeholder='Enter your name here' name="name" onChange={this.handleChanges}/><br />
< ValidateName content={this.state.name} />
<button type='submit'>Sign Up</button>
<button>Cancel</button>
</div>
</form>
</Modal>}
</div>
);
}
}
export default Home;
// Modal.js
import React, { Component } from 'react';
class Modal extends Component {
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
export default Modal;
//validation.js
class ValidateName extends Component {
err1 ='Please enter any name';
err2 = 'Use only letters in this field';
render() {
const ren = this.props.content.length === 0 ? (<p>{this.err1}</p> ) :
(this.props.content.match(/[a-zA-Z]+$/) ? '' : <p>{this.err2}</p>)
return ren;
}
}
Please suggest an idea to set name_error as 'Please enter any name' or 'Use only letters in this field' when user enters wrong input
State should be like this,
constructor(props){
super(props);
this.state = {
show: false,
name: "",
name_error: ""
}
}
And Also the update state using callback. It would be helpful,
this.setState({[name] : value},() =>{console.log(this.state)})
I am working on a login form where each input is created dynamically as a field.
This is my Login.js file:
import _ from 'lodash';
import React, { Component } from 'react';
import {reduxForm, Field } from 'redux-form';
import{ Link } from 'react-router-dom';
import FIELDS from './loginFields';
import LoginField from './LoginField'
import { connect } from 'react-redux';
import * as actions from '../../actions'
class LoginForm extends Component {
constructor(){
super();
this.state={
username: '',
password: ''
};
};
handleChange = (e)=>{
this.setState({username: e.target.value, password: e.target.value});
};
renderFields(){
return _.map(FIELDS, ({ label, name, type })=> {
return <Field onChange={this.handleChange} className='purple-text' key={name} component={ LoginField } type={type} label={label} name={name} />
});
};
render(){
const { username, password } = this.state;
const isEnabled = username.length > 0 && password.lenth>7;
return (
<div className='valign-wrapper row login-box' style={{marginTop:'100px'}}>
<div className='col card hoverable s10 pull-s1 m6 pull-m3 l4 pull-l4'>
<form method='POST' action='/api/login'>
<div className = 'card-content'>
<span className='card-title purple-text' style={{textAlign:'center'}}>Login<a href='/register'> Not a member? sign up!</a></span>
<div className='center-align row'>
<li key='google' style={{marginLeft: '30px'}} className='col m6 center-align white-text darken-3'><a className='white-text' href='/auth/google'><img alt="" src="https://img.icons8.com/cute-clipart/64/000000/google-logo.png"/></a></li>
<li key='facebook' className='col center-align white-text darken-3'><a className='white-text' href='/auth/facebook'><img alt = "" src="https://img.icons8.com/cute-clipart/64/000000/facebook-new.png"/></a></li>
</div>
<div className='row input-field col s12'>
{this.renderFields()}
<Link to='/' className='purple btn-flat left white-text'>Back</Link>
<button disabled={!isEnabled} type='submit' className='purple btn-flat right white-text'>Login
<i className='material-icons right'>done</i>
</button>
</div>
</div>
</form>
</div>
</div>
);
};
};
function validate(values){
const errors = {};
_.each(FIELDS, ({name})=>{
if(!values[name]){
errors[name] = 'You must enter a value!'
}
});
return errors;
};
const form = reduxForm({
validate,
form: 'LoginForm'
});
export default connect(null, actions)(form(LoginForm));
Here is loginFields.js
export default
[
{ label: 'Username', name: 'username', type: 'text'},
{ label: 'Password', name: 'password', type: 'password'}
];
and here is LoginField.js
import React from 'react';
export default ({ input, label, type, meta })=>{
return(
<div>
<label className='purple-text'>{label}</label>
<input {...input} type={type} style= {{marginBottom: '5px'}}/>
<div className = "red-text" style={{ marginBottom: '20px'}}>
{meta.touched && meta.error}
</div>
</div>
);
};
I am having trouble properly setting onChange and my constructor to disable the login button until all fields are filled. I have been able to disable the button until a single input has started to be filled in, not disabled at all, and not enabled at all. but have not been able to achieve the desired outcome.
I have tried using lodash to map over each field grabbing values by the input name property, and moving functions around.
Any help would be greatly appreciated, if i can provide any more information for this question please let me know.
The initial problem I see is the onChange function will update state for both password and username whenever either of them is changed. The function takes the event and does not distinguish as to which input is the target. You can pass an additional parameter from the Field that includes the field name, or you can check the target's id or something so you know which input's state should be updated.
In LoginForm
handleChange = (e, name)=>{
this.setState({[name]: e.target.value});
};
You also need to pass the onChange callback down to the actual input in LoginField.js
import React from 'react';
export default ({ name, label, type, meta, onChange, ...props })=>{
return(
<div>
<label className='purple-text'>{label}</label>
<input onChange={(e) => onChange(e, name)} {...props} type={type} style= {{marginBottom: '5px'}}/>
<div className = "red-text" style={{ marginBottom: '20px'}}>
{meta.touched && meta.error}
</div>
</div>
);
};
Here's a codeSandbox.
just adding this as an answer in case anyone else comes across this issue.
after tons of digging I finally found documentation. in redux form has a built in prop called {invalid} which checks against the validate function. instead of messing with state all i had to do was add
const {invalid} = this.props;
inside the render method. constructor and handle change and onChange were no longer necessary.. then.
<button disabled={invalid}>
I have a separate module that I'm working on, this module is meant to contain formik supporting HTML input elements.
The issue is I'm unable to use the useFields() hook since my module component doesn't connect to the formik context.
Here's my component that resides in a different module:
import React from "react";
import PropTypes from "prop-types";
import { useField } from "formik";
export function TextField({ label, ...props }) {
const [field, meta] = useField(props);
return <input {...field} {...meta} />;
}
TextField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string,
showErrors: PropTypes.bool
};
TextField.defaultProps = {
label: "",
showErrors: true
};
export default TextField;
and here is my Formik form:
<Formik
initialValues={{
firstName: "firstName"
}}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
>
{formik => (
<Form>
<TextField name="firstName" />
<button type="submit">Submit</button>
</Form>
)}
</Formik>
No matter what I do I get the following error:
TypeError: Cannot read property 'getFieldProps' of undefined
Anyone have an idea what I'm doing wrong?
Looking at the useField docs I would expect:
<input {...field} {...props} />
The input component does not expect the {...meta} props.
other than that I could not reproduce your error.
I am using the bootstrap-typehead:https://github.com/ericgio/react-bootstrap-typeahead and I cannot figure out why this package is freaking out. Whats wrong with this code that it gives me this error:
Failed prop type: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.
import React, { Component } from "react";
import Dropdown from "../../bootstrap/Dropdown";
import RealmAPIs from "../../../API/realms/RealmAPI";
import {Typeahead} from 'react-bootstrap-typeahead';
import AutoComplete from "../../bootstrap/AutoComplete";
// import RealmAPI from '../../../API/realms/RealmAPI';
var options = [
'John',
'Miles',
'Charles',
'Herbie',
];
export default class FindCharacter extends Component {
state = {
realmName: "",
characterName: "",
realms: []
};
setRealmName = value => {
this.setState({ realmName: value });
};
componentDidMount() {
// let realms = [...this.state.realms];
// RealmAPIs.getAllRealms().then(response =>
// console.log(response.realms.map(value => {}))
// );
}
render() {
return (
<div>
<form className="form-inline justify-content-md-center">
<div className="form-group mb-2">
{/* <Dropdown setRealmName={this.setRealmName}/> */}
<Typeahead
labelKey="name"
placeholder="Type a realm"
onChange={selected => {
console.log(selected);
}}
options={
options
}
/>
</div>
<div className="form-group mx-sm-3 mb-2">
<input
type="text"
className="form-control"
id="characterName"
placeholder="Enter Character Name"
/>
</div>
<button
type="submit"
className="btn btn-primary mb-2"
onClick={e => {
e.preventDefault();
console.log(this.state.name);
}}
>
Submit
</button>
</form>
</div>
);
}
}
Upgrading to v3.2.3 of react-bootstrap-typeahead should solve the issue.
I'm not sure why, but React v16.5 (I think?) started triggering the warning. There was an issue tracking it as well as a PR fixing it.
I am trying to create a customer details form in react (currently using react-json-form) where I can reuse the values in the inputs to create a saved file that the app can refer to. I have created the form and can output the results but I am unsure how to save the input values for future use or call them back once they are saved.
If anyone has any suggestions or examples of a form that does this then I would be greatly appreciative.
My code is as follows:
import React, { Component } from 'react';
import JSONTree from 'react-json-tree';
import { BasicForm as Form, Nest, createInput } from 'react-json-form';
const Input = createInput()(props => <input type="text" {...props} />);
const UserFields = () => (
<section>
<h3>User</h3>
<div>Name: <Input path="name" /></div>
<div>Email: <Input path="email" /></div>
</section>
);
export default class ExampleForm extends Component {
state = { data: {} };
updateData = data => this.setState({ data });
render() {
return (
<Form onSubmit={this.updateData}>
<Nest path="user">
<UserFields />
</Nest>
<button type="submit">Submit</button>
<JSONTree data={this.state.data} shouldExpandNode={() => true} />
</Form>
);
}
}
A more simple solution would be to use a form, like a semanti-ui-react form, store the information to the state onChange, then convert the info to JSON for storage.
import { Form, Button } from 'semantic-ui-react'
export default class App extends Component {
constructor() {
super()
this.state = {
name: "",
email: ""
}
}
handleChange = (e, {name, value}) => {
console.log(name, value)
this.setState({[name]: value})
}
render() {
return (
<div>
<Form onSubmit={this.sendDataSomewhere}>
<Form.Field>
<Form.Input name="name" value={this.state.name} onChange={this.handleChange}/>
</Form.Field>
<Form.Field>
<Form.Input name="email" value={this.state.email} onChange={this.handleChange}/>
</Form.Field>
<Button type="submit">Submit</Button>
</Form>
</div>
)
}
}
I use a dynamic method of receiving the input from different fields using the name and val attributes. The values captured in state are then accessible by this.state.whatever
Hope this helped