Render the form input binding from external function in react - javascript

I am trying to bind the input from the function, as per the below code
class ProfessionalLearningAction extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.ensureDataFetched();
}
handleChange(e) {
let change = {}
change[e.target.name] = e.target.value
this.setState(change)
}
render() {
return (
<div className="container">
<h1>Edit/Add Professional Learning</h1>
<p>This component demonstrates Add/Edit data from the server and working with URL parameters.</p>
<br /><br />
{renderProfessionalLearningTable(this.props)}
</div>
);
}
}
function renderProfessionalLearningTable(props) {
return (
<form className="container">
<div className="form-row">
<div className="form-group col-sm-6">
<label>Course Name *</label>
<input type="text" className="form-control" value={props.professionalLearnings.courseName || ''} onChange={ props.handleChange }
aria-describedby="Course Name" placeholder="Enter a course name" />
</div>
</div >
</form >
);
}
Keep getting the 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`.
Since I know from the error message that I need to define handlechange event. I tried the below code
<input type="text" className="form-control" value={props.professionalLearnings.courseName || ''} onChange={this.handleChange}
aria-describedby="Course Name" placeholder="Enter a course name" />
Since I know it is a function call which is external to the class. How can I fix this error?

Its because you don't pass onchange props. And make sure whenever you made a component it should start with Capital Name(ReactJS component names must begin with capital letters?).
class ProfessionalLearningAction extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.ensureDataFetched();
}
handleChange(e) {
let change = {}
change[e.target.name] = e.target.value
this.setState(change)
}
render() {
return (
<div className="container">
<h1>Edit/Add Professional Learning</h1>
<p>This component demonstrates Add/Edit data from the server and working with URL parameters.</p>
<br /><br />
<RenderProfessionalLearningTable {...this.props} handleChange={this.handleChange}/> //Pass the handlechange component
</div>
);
}
}
function RenderProfessionalLearningTable(props) {
return (
<form className="container">
<div className="form-row">
<div className="form-group col-sm-6">
<label>Course Name *</label>
<input type="text" className="form-control" value={props.professionalLearnings.courseName || ''} onChange={ props.handleChange }
aria-describedby="Course Name" placeholder="Enter a course name" />
</div>
</div >
</form >
);
}

Related

Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

I'm trying to create a sign up form with an input for a users address. The address input uses the google autocomplete address api.
I'd like to be able to keep it as a Formik field, so I can use Yup validation on it.
The address input component looks like
// Google.jsx
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
/* global google */
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.autocompleteInput = React.createRef();
this.autocomplete = null;
this.handlePlaceChanged = this.handlePlaceChanged.bind(this);
}
componentDidMount() {
this.autocomplete = new google.maps.places.Autocomplete(this.autocompleteInput.current,
{"types": ["address"]});
this.autocomplete.addListener('place_changed', this.handlePlaceChanged);
}
handlePlaceChanged(){
const place = this.autocomplete.getPlace();
console.log(place);
}
render() {
return (
<Field ref={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder="" />
);
}
}
export default SearchBar;
And my Form component looks like:
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import SearchBar from "./Google";
const LoginSchema = Yup.object().shape({
fName: Yup.string().required("Please enter your first name"),
address: Yup.string().required("invalid address"),
});
class Basic extends React.Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-lg-12">
<Formik
initialValues={{
fName: "",
postal: "",
}}
validationSchema={LoginSchema}
onSubmit={(values) => {
console.log(values);
console.log("form submitted");
}}
>
{({ touched, errors, isSubmitting, values }) =>
!isSubmitting ? (
<div>
<div className="row mb-5">
<div className="col-lg-12 text-center">
<h1 className="mt-5">LoKnow Form</h1>
</div>
</div>
<Form>
<div className="form-group">
<label htmlFor="fName">First Name</label>
<Field
type="text"
name="fName"
className={`mt-2 form-control
${touched.fName && errors.fName ? "is-invalid" : ""}`}
/>
<ErrorMessage
component="div"
name="fName"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<Field name="address" component={SearchBar} placeholder="" />
<ErrorMessage
component="div"
name="address"
className="invalid-feedback"
/>
</div>
<button
type="submit"
className="btn btn-primary btn-block mt-4"
>
Submit
</button>
</Form>
</div>
) : (
<div>
<h1 className="p-3 mt-5">Form Submitted</h1>
<div className="alert alert-success mt-3">
Thank for your connecting with us.
</div>
</div>
)
}
</Formik>
</div>
</div>
</div>
);
}
}
export default Basic;
This returns an error of "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?".
Which is coming from my address input component at: <Field ref={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder="" />
Everything else is working, I just need to get past this last hurdle and I'll be good from here.
I will begin looking into the docs, but I'm unfortunately in a rush to get this done so I figured I'd try my luck here!
Any help is greatly appreciated! Thank you!
The Field component does allow you to get access to the underlying element, but not via ref. You need to pass it to innerRef instead.
<Field innerRef={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder=""/>
See documentation of Field here: https://formik.org/docs/api/field#innerref

Pass data between two react components in MERN stack

I am trying to build an E commerce web app where I handle the payments. First I need to take shipping information. I have successfully inserted data into mongo but I could not pass the data into another js right away from a single click.
Here is my create address page.
import React, { Component } from 'react'
import '../App.css'
import axios from 'axios'
export default class CreateAddress extends Component {
constructor(props) {
super(props);
this.state = {
address: "",
city: "",
phoneNo:"",
postalCode: "",
country: ""
}
}
handleInputChange = (e)=>{
const {name,value} = e.target;
this.setState({
...this.state,
[name]:value
})
}
onSubmit = (e)=>{
e.preventDefault();
const {address,city,phoneNo,postalCode,country} = this.state;
const data = {
address: address,
city: city,
phoneNo: phoneNo,
postalCode: postalCode,
country: country
}
console.log(data)
axios.post("http://localhost:5000/address/save",data).then((res)=>{
if(res.data.success){
this.setState(
{address: "",
city: "",
phoneNo:"",
postalCode: "",
country: ""}
)
alert("Shipping info added successfully");
}
})
}
render() {
return (
<div className="row wrapper">
<div className="col-10 col-lg-5">
<form className="shadow-lg" >
<h1 className="mb-4">Shipping Info</h1>
<div className="form-group">
<label htmlFor="address_field">Address</label>
<input
type="text"
id="address_field"
className="form-control"
name='address'
value={this.state.address}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="city_field">City</label>
<input
type="text"
id="city_field"
className="form-control"
name='city'
value={this.state.city}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="phone_field">Phone No</label>
<input
type="phone"
id="phone_field"
className="form-control"
name='phoneNo'
value={this.state.phoneNo}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="postal_code_field">Postal Code</label>
<input
type="number"
id="postal_code_field"
className="form-control"
name='postalCode'
value={this.state.postalCode}
onChange={this.handleInputChange}
required
/>
</div>
<div className="form-group">
<label htmlFor="country_field">Country</label>
<input
type = "text"
id="country_field"
className="form-control"
name='country'
value={this.state.country}
onChange={this.handleInputChange}
required
/>
</div>
<button
id="shipping_btn"
type="submit"
className="btn btn-block py-3"
onClick={this.onSubmit}
>
SAVE
</button>
<button id="shipping_btn" type="submit" className = "btn btn-block py-3"><a href = "/confirm" style={{textDecoration:'none',color:'white'}}>PROCEED</a></button>
</form>
</div>
</div>
)
}
}
Once I clicked the PROCEED button the data should direct into /confirm page
You can use react-router to move between different routes in React. Also, in your code when you are sending a post request on onSubmit method, you are clearing the state information, which I think needs to be handled in the confirm page since you need the data in that page also.
Here's a codesandbox demonstrating how you can send the information to another page, using react-router. I've used the functional components, but the concepts works for the class based components also.
note: also you should use onSubmit handler of the form to submit values rather than using the onClick method of the button. Otherwise you force users to click the button to submit the form.
You can use history API to pass data as a URL and grab it into confirm page.
history.push(`/confirmPage?city=${city}&postalCode=${postalCode}`)
in confirmPage.js you will get that data as props
export default class ConfirmPage extends Component{
render(){
const {city, postalCode } = this.props.location.query;
return (
<div>{postalCode}</div>
)
}

How to create a dynamic input type component

I'm developing a dynamic component where the input can be used for several types: text, password, number, date, etc. The idea is to use this input, no matter the type and where to implement it, as long its adaptable. I thought using state was a nice idea, but I have no clue how to do this. Any thoughts?
import React, { Component } from 'react';
import './styles.css';
export default class InputField extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
password: false,
type: ''
}
}
render () {
return (
<div>
<label className='f-size'>{this.state.name}</label>
<input
className='input'
name={this.state.name}
placeholder={this.state.name}
value={this.props.value}
type={this.state.type}
onChange={this.props.onChange}
/>
<span className="errorMessage">{this.props.error}</span>
<span className="errorMessage">{this.props.missField}</span>
</div>
)
}
}
Thank you!
I personally think you should control this via props, seeing as the value will only be meaningful to the Input's parent.
I used this
const InputField = ({
name,
placeholder,
value,
type,
onChange,
error,
missField
}) => (
<div>
<label className="f-size">{name}</label>
<input
className="input"
name={name}
placeholder={placeholder}
value={value}
type={type}
onChange={onChange}
/>
<span className="errorMessage">{error}</span>
<span className="errorMessage">{missField}</span>
</div>
);
Parent component:
class App extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
state = {
value: '',
password: '',
};
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
render() {
return (
<div className="App">
<InputField
value={this.state.value}
type="number"
name="value"
onChange={this.handleChange}
/>
<InputField
value={this.state.password}
type="password"
name="password"
onChange={this.handleChange}
/>
</div>
);
}
}
Code Sandbox: https://codesandbox.io/s/y4ljv75k9
Edited to used a stateless component. Not sure if you want state to handle error messages but from your example, this is a valid solution.
<InputField type="text" />
<InputField type="password" />
<input
className='input'
name={this.state.name}
placeholder={this.state.name}
value={this.props.value}
type={this.props.type}
onChange={this.props.onChange}
/>
I would use props to change the type and manage the component.
You could then control the component from a form definition
You should use props not state, so you can pass
<InputType type="text" />
<InputType type="password" />
<InputType type="number" />
and for the other params you can use props also.
You could use this.props.type but the standard jsx input component is already dynamic as you can see from my example below :
var root = document.getElementById('root');
class InputField extends React.Component {
render() {
return (
<div>
<input type={this.props.type} />
</div>
)
}
}
class App extends React.Component {
render() {
return (
<div>
<input type='date' />
<InputField type='password'/>
</div>
)
}
}
ReactDOM.render(<App />, root)
<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>
<div id='root'></div>
Is there a reason why you would like to use a custom input component?

A component is changing an uncontrolled input of type email to be controlled. Input elements should not switch from uncontrolled to controlled

I am getting this error, I am new in react. I have seen other posts but still could not resolve the issue. help will be appreciated. thanks
Warning: A component is changing an uncontrolled input of type email to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info
in input (created by Edit)
in div (created by Edit)
in div (created by Edit)
in form (created by Edit)
in div (created by Edit)
in Edit
import React from 'react'
import ReactDom from 'react-dom'
export default class Edit extends React.Component{
constructor(){
super();
this.state={
name: '',
email: '',
password: '',
};
}
componentWillMount(){
let id=this.props.id;
axios.get('/api/users/'+id).then(response => {
var user= response.data;
this.setState({
name: user.name,
email: user.email,
})
}).catch(error => {
console.log(error)
})
}
handleNameChange(e){
this.setState({
name: e.target.value
})
}
handleEmailChange(e){
this.setState({
email: e.target.value
})
}
handlePasswordChange(e){
this.setState({
password: e.target.value
})
}
handleSubmit(e){
e.preventDefault();
console.log(this.state)
axios.post('/api/users',this.state).then(response => {
console.log(response)
}).then(error => {
console.log(error)
})
}
render(){
return(
<div>
<h2> Add New User </h2>
<form className="form-horizontal" onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Name:</label>
<div className="col-sm-10">
<input type="text" className="form-control" id="name" placeholder="Enter name" value={this.state.name}
onChange={ this.handleNameChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Email:</label>
<div className="col-sm-10">
<input type="email" className="form-control" id="email" placeholder="Enter email" value={this.state.email}
onChange={ this.handleEmailChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="email">Password:</label>
<div className="col-sm-10">
<input type="password" className="form-control" id="password" placeholder="Enter password" value={this.state.password}
onChange={ this.handlePasswordChange.bind(this) }/>
</div>
</div>
<div className="form-group">
<button type="submit" className="btn btn-default">Update</button>
</div>
</form>
</div>
)
}
}
if (document.getElementById('edit')) {
var id=$("#edit").data("id");
ReactDom.render(<Edit id={id}/>, document.getElementById('edit'))
}
You are using value attribute to set default value while rendering, it makes your input controlled input, then when updating your states you are trying to change your input values again which will make it uncontrolled one. basically for your code to succeed you need to use defaultValue attribute in your rendered code instead of value attr. because when you are setting value to input from html it can't be later changed.
do this instead for your input elements:
<input type="email" className="form-control" id="email" placeholder="Enter email" defaultValue={this.state.email} />
here is a source from react docs on issue:
https://reactjs.org/docs/uncontrolled-components.html
The error is occuring because when you're doing setState inside componentDidMount, the value returned seems to be undefined.
if(user.name!==undefined && user.email!==undefined){
this.setState({
name: user.name,
email: undefined,
})
}
This will prevent the values in the state from becoming undefined and you won't have that error anymore.

React Select doesn't update value on clicking option

I have a React Select form, with options loaded after an API call. The initial state value of workshop is set, and it updates after an option is clicked in the form. However, the Select's view does not update. What is more, when the form is submitted, the workshop is successfully saved into the database. What should I do ?
renderForm() {
return (
<section className="col-md-12 col-sm-12">
<form className="col-md-12 col-sm-12"
onSubmit={ this.handleSubmit }>
// other form-groups
<div className="form-group">
<label>
Region:
<input className="form-control"
type="text"
value={ this.state.value }
onChange={ this.handleRegionChange } required />
</label>
</div>
<div className="form-group">
<label>
Workshop:
</label>
<Select name="form-field-workshop"
value={ this.state.workshop }
onChange={ this.handleWorkshopChange }
options={ this.renderFormWorkshops() }
clearable={ false }
searchable={ false }
required />
</div>
<input className="btn btn-default"
type="submit"
value="Submit" />
</form>
</section>
);
}
// handles the change of state when an option is selected
handleWorkshopChange(value) {
this.setState({
workshop: value.label
});
}
// displays the options in the Select form
renderFormWorkshops() {
return _.map(this.props.workshops.workshops, (it) => {
return (
{ value: it.id, label: it.name }
);
});
}
handleWorkshopChange(value) {
this.setState({
workshop: value.value
});
}
Do you have it setup correctly in your constructor? There are likely some context issues happening here, this is the correct form to bind context:
constructor(props) {
super(props);
this.handleWorkshopChange = this.handleWorkshopChange.bind(this);
}
If you aren't using arrow functions, you need to bind context for situations that rely on using this methods.
Also, you have a stray parentheses here: value={ this.state.workshop) }

Categories