there are two ways i am rendering form fields
first way is directly putting them inside a render
form.tsx
import * as React from 'react';
export interface FormProps {
}
export interface FormState {
personal: any
}
class Form extends React.Component<FormProps, FormState> {
constructor(props: FormProps) {
super(props)
this.state = {
personal: {
firstName: "",
lastName: "",
}
}
}
handleChange = (event: any) => {
let personal = { ...this.state.personal };
personal[event.target.name] = event.target.value;
this.setState({ personal })
}
render() {
return (
<div>
<h1>first name</h1>
<input type="text" name="firstName" value={this.state.personal.firstName} onChange={this.handleChange} />
<h1>last name</h1>
<input type="text" name="lastName" value={this.state.personal.lastName} onChange={this.handleChange} />
</div>
);
}
}
export default Form;
the other way
form.tsx
import * as React from 'react';
export interface FormProps {
}
export interface FormState {
personal: any
}
class Form extends React.Component<FormProps, FormState> {
constructor(props: FormProps) {
super(props)
this.state = {
personal: {
firstName: "",
lastName: "",
}
}
}
count = 0;
handleChange = (event: any) => {
let personal = { ...this.state.personal };
personal[event.target.name] = event.target.value;
this.setState({ personal })
}
renderFields = () => {
this.count++;
console.log('render fields call count = ', this.count);
return (
<div>
<h1>first name</h1>
<input type="text" name="firstName" value={this.state.personal.firstName} onChange={this.handleChange} />
<h1>last name</h1>
<input type="text" name="lastName" value={this.state.personal.lastName} onChange={this.handleChange} />
</div>
)
}
render() {
return (
this.renderFields()
);
}
}
export default Form;
issue here is render fields being called on every state change and that is understandable but will this affect my performance
what i am going to do next is turn a json schema into form
checking what type of field it is and accordingly create the input field and many more things
example json is
"personal":{
"fields":[
{
"label":"name",
"type":"text"
},
{
"label":"id",
"type":"number"
}
]
}
It wont't actually hamper performance. Because there won't be actual dom manipulation if form's DOM attribute has not changed. To enhance more you can have pure component. To make custom formField where type and label can be changed. Have a custom form field component.
Related
I am trying to display input value on submit. But it seems to be not working. I don't have any errors but nothing being rendered. What is wrong with the code?
import React from 'react';
import { Component } from 'react';
class App extends Component {
constructor (props) {
super(props)
this.state = {
city : ""
}
}
handleSubmit = (event)=> {
event.preventDefault();
this.setState ({
city : event.target.value
})
}
render () {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type = "text" city = "city_name" />
<button type="submit">Get Weather</button>
{this.state.city}
</form>
</div>
)
}
}
export default App;
<form
onSubmit={e=>{
e.preventDefault()
console.log(e.target[0].value)
}}>
<input type="text"/>
<button type="submit">dg</button>
</form>
that works for me very well
Remember onSubmit target:
Indicates where to display the response after submitting the form. So you can get inner elements (and their corresponding values) like normal javascript code.
const city = event.target.querySelector('input').value
handleSubmit = (event) => {
event.preventDefault();
this.setState ({ city })
}
I guess you want it to get work like below. But this is not the only way to get it done.
import React from "react";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
city: ""
};
}
handleSubmit = (event) => {
const formData = new FormData(event.currentTarget);
event.preventDefault();
let formDataJson = {};
for (let [key, value] of formData.entries()) {
formDataJson[key] = value;
}
this.setState(formDataJson);
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" name="city" />
<button type="submit">Get Weather</button>
{this.state.city}
</form>
</div>
);
}
}
export default App;
Code sandbox => https://codesandbox.io/s/eager-oskar-dbhhu?file=/src/App.js
constructor() {
super();
this.state = {
name: "",
name1: "",
};
}
change = () => {
this.setState({ name: this.state.name1 });
};
handleChange = (e) => {
this.setState({ name1: e.target.value });
};
render() {
return (
<div>
<input
type="text"
placeholder="Enter your name"
onChange={this.handleChange}
></input>
<button onClick={this.change}>Click Me!</button>
<h4>Hello! {this.state.name}</h4>
</div>
);
}
This is what I did but feels like it is nonsense on actual webpage even it works. Is there a better way to take input from user?
Why you need name and name1 in state. Just name should be fine. See the below code if that helps
I am not sure why you handle a event in button. May you can use a form with OnSubmit.
import React from "react";
import "./style.css";
export default class App extends React.Component {
constructor() {
super();
this.state = {
name: "",
};
}
render() {
return (
<div>
<input
type="text"
placeholder="Enter your name"
onChange={(e) => this.setState({name: e.target.value})}
/>
<button>Click Me!</button>
<h4>Hello! {this.state.name}</h4>
</div>
);
}
}
Your onChange in the input would be
onChange={event => this.handleChange(event)}
And for the button it would be
onChange{() => this.change()}
We would not require 2 states for the name but we would need one variable to store the name and second variable to let the component know that name has been update. We need the second variable because on button click only the name has to be updated(as per the code mentioned).The component would re-render when a state is updated. Below code might be helpful.
class Content extends React.Component {
constructor(){
super()
this.state = {
name: "",
}
this.name=''
}
change = () => {
this.setState({name: this.name})
}
handleChange = (e) => {
this.name=e.target.value
}
render(){
return(
<div>
<input type = "text" placeholder="Enter your name" onChange={this.handleChange}></input>
<button onClick={this.change}>Click Me!</button>
<h4>Hello! {this.state.name}</h4>
</div>
)
}}
Here is a version with React Hooks:
import React, { useState } from 'react';
export default function App() {
const [name, setName] = useState('');
const handleNameChange = (e) => {
const nameInput = e.target.value;
setName(nameInput);
};
return (
<div>
<input
type="text"
placeholder="Enter your name"
onChange={(e) => handleNameChange(e)}
></input>
<button>Click Me!</button>
<h4>Hello! {name}</h4>
</div>
);
}
I have two components in two files: Firebase and Recipe. I call in Recipe a function createRecipe from Firebase file.
When I call this.setState({ recipies }) an error occurs. I searched a solution and tried to bind context according to results here.
Firebase file:
class Firebase {
constructor () {
app.initializeApp(config)
// TRIED
this.createRecipe = this.createRecipe.bind(this)
this.auth = app.auth()
this.db = app.database()
}
state = {
recipies: {}
}
createRecipe = recipe => {
const recipies = {...this.state.recipies}
recipies[`recipe-${Date.now()}`] = recipe
this.setState({ recipies })
}
}
export default Firebase
Recipe file:
import { withAuthorization } from '../Session'
import { withFirebase } from '../Firebase'
class Recipe extends Component {
state = {
name: '',
image: '',
ingredients: '',
instructions: ''
}
handleChange = event => {
const { name, value } = event.target
this.setState({ [name]: value })
}
handleSubmit = event => {
event.preventDefault()
const recipe = { ...this.state }
// TRIED
this.props.firebase.createRecipe(recipe)
this.props.firebase.createRecipe(recipe).bind(this)
this.resetForm(recipe)
}
render () {
return (
<div>
<div className='card'>
<form
// TRIED
onSubmit={this.handleSubmit>
onSubmit={() => this.handleSubmit>
onSubmit={this.handleSubmit.bind(this)}>
<input value={this.state.name} type='text' name='nom' onChange={this.handleChange} />
<input value={this.state.image} type='text' name='image' onChange={this.handleChange} />
<textarea value={this.state.ingredients} rows='3' name='ingredients' onChange={this.handleChange} />
<textarea value={this.state.instructions} rows='15' name='instructions' onChange={this.handleChange} />
<button type='submit'>Add recipe</button>
</form>
</div>
</div>
)
}
}
const condition = authUser => !!authUser
export default withAuthorization(condition)(withFirebase(Recipe))
Do you have an idea about what's going wrong ? Many thanks.
class Firebase doesn't extend React.component so it doesn't know what state is. This is expected, extend React.component or use state hooks useState().
You are not using react in your Firebase component
How to fix:
import React, {Component }from 'react';
class Firebase extends Component {
constructor(props){
super(props)
// your code
}
// your code
render(){
return null; // this won't change anything on your UI
}
}
I want to change state by the value of input but it is undefined.
It should work, the value is for every HTML tag so what is wrong with my code??
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
firstName: " "
};
this.handleChange = this.handleChange(this);
}
handleChange(event) {
this.setState({
firstName: event.target.value
});
}
render() {
return (
<form>
<input
type="text"
placeholder="Firstname"
onChange={this.handleChange}
/>
<h1>{this.state.value}</h1>
</form>
);
}
}
export default App;
There's no value stored in your state, so it is undefined, and you do not correctly bind this to your handler. Output this.state.firstName instead:
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
firstName: " "
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
firstName: event.target.value
});
}
render() {
return (
<form>
<input
type="text"
placeholder="Firstname"
onChange={this.handleChange}
/>
<h1>{this.state.firstName}</h1>
</form>
);
}
}
export default App;
First try to bind this for handleChange using an arrow function. Also use the state variable to set the value property of the input .
Sandbox link: https://codesandbox.io/s/react-basic-example-qmyw5
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
firstName: " "
};
}
handleChange = (event) => {
this.setState({
firstName: event.target.value
});
}
render() {
return (
<form>
<input
type="text"
placeholder="Firstname"
onChange={this.handleChange}
value={this.state.firstName}
/>
<h1>{this.state.firstName}</h1>
</form>
);
}
}
export default App;
I'm using React v16 (latest) and I'm trying to create a general form component, that uses props.children.
export class MyForm extends React.Component {
render() {
return (
<div>
<div className="form">
<h3>{this.props.formName}</h3>
<div>
{React.Children.map(this.props.children, t => {return <span>{t}<br></br></span>;})}
</div>
<input type="button" value={this.props.formName} onClick={this.handleClick}/>
</div>
</div>
)
}
}
I want to create small form that just are able to create a meaningful json object and send it in POST
This is an example of such usage:
<MyForm>
<input type="text" name="a1"></input>
<input type="text" name="a2"></input>
</MyForm>
And I want to have many such small forms. Problem is I want each child (props.children) to have an onChange event -
onChange(event) // name is "a1" or "a2", like in the example aboce
{
var obj = {};
obj[name]=event.target.value;
this.setState(obj);
}
-so that I don't need to manually add onChange for each such child
-I guess another solution is to create a component, but I want the form to be flexible for each kind of sub-element (input text, text area, radio buttons,...) and I just want them all to have similar onChange that will set the name of the component and its value to the state...
I tried adding an onChange property in consturctor and in different hooks, but got:
cannot define property 'onChange', object is not extensible
So when are where (if at all) can I add an onChange dynamically to props.children
This is a great use case for a Higher Order Component. You can use a HOC to wrap and add the onChange prop to any component:
const WithOnChange = WrappedComponent => {
return class extends Component {
onChange = e => {
const obj = {};
obj[name]=e.target.value;
this.setState(obj);
}
render() {
return <WrappedComponent {...this.props} onChange={this.onChange} />
}
}
}
...
import Input from './Input';
class MyForm extends Component {
render() {
return (
<form>
...
<Input type="text" name="a1" />
...
</form>
)
}
}
export default MyForm;
....
import WithOnChange from './WithOnChange';
const Input = (props) => (
<input {...props} />
);
export default WithOnChange(Input);
EDIT:
Another option is to move your children map into a higher order component and then create a custom <Form /> component:
const Form = () => {
return <form>{this.props.children}</form>
};
export default WithOnChange(Form);
const WithOnChange = WrappedComponent => {
return class extends Component {
onChange = e => {
const obj = {};
obj[name] = e.target.value;
this.setState(obj);
}
render () {
const children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, { onChange: this.onChange });
});
return <WrappedComponent {...this.props}>{children}</WrappedComponent>
}
}
}
#user967710, can you please test the following code:
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(event) {
debugger;
var obj = {};
obj.name = event.target.value;
this.setState(obj);
}
<MyForm formName="myForm">
<input type="text" name="a1" onChange={this.onChange}></input>
<input type="text" name="a2" onChange={this.onChange}></input>
</MyForm>