I have a form where user gets to post the topics. Beneath that the posted topic is shown. When there is the validation error in one of the topic name or type field then all the name or type field is shown with name or topic required. why is that so? The validation in onChange event is not behaving normally.
Here is the code
This if for the validation
export const validate = values => {
const errors = {}
const requiredFields = {
topic_name: 'Topic Name',
topic_type: 'Topic Type',
}
for (const key in requiredFields) {
if(requiredFields.hasOwnProperty(key)) {
if(!values[key]) {
errors[key] = `${requiredFields[key]} is required`
}
}
}
return errors
}
topic
class Topic extends React.Component {
constructor(props) {
super(props);
this.state = {
customTopic: [],
visible: false,
id: '',
topic_name: this.props.match.params.topic || '',
topic_type: ''
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.props.fetchCustomTopic();
}
handleChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
simplePlainTable = topics => (
<DataTable plain>
<TableHeader>
<TableRow>
<TableColumn>Topic Name</TableColumn>
<TableColumn>Topic Type</TableColumn>
<TableColumn>Action</TableColumn>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableColumn>
<Field
placeholder="Topic Name"
name="topic_name"
id="topic_name"
value={this.state.topic_name}
onChange={this.handleChange}
className="input-field"
type="text"
fullWidth
component={TopicNameField}
required
/>
</TableColumn>
<TableColumn>
<Field
placeholder="Topic Type"
name="topic_type"
id="topic_type"
value={this.state.topic_type}
onChange={this.handleChange}
required
className="input-field"
type="text"
fullWidth
component={TopicTypeField}
/>
</TableColumn>
<TableColumn>
<Button
icon
tooltipLabel="Add"
onClick={e => this.addCustomTopic(e)}>
add
</Button>
</TableColumn>
</TableRow>
{topics &&
topics.customTopic.map((obj, i) => (
<TableRow key={i} id={obj.id}>
<TableColumn>
<Field
placeholder="Topic Name"
name="topic_name"
id="topic_name"
defaultValue={obj.topic_name}
onBlur={e =>
this.updateCustomTopic(
{ topic_name: e.target.value },
obj.id
)
}
required
className="input-field"
type="text"
fullWidth
component={TopicNameField}
/>
</TableColumn>
<TableColumn>
<Field
placeholder="Topic Type"
name="topic_type"
id="topic_type"
defaultValue={obj.topic_type}
onBlur={e =>
this.updateCustomTopic(
{ topic_type: e.target.value },
obj.id
)
}
required
className="input-field"
type="text"
fullWidth
component={TopicTypeField}
/>
</TableColumn>
<TableColumn>
<Button
icon
tooltipLabel="Delete"
onClick={() => this.show(obj.id)}>
delete
</Button>
</TableColumn>
</TableRow>
))}
</TableBody>
</DataTable>
);
render() {
const { topics } = this.props;
return (
<div className="container">
{this.simplePlainTable(topics)}
{this.dialogContainer()}
</div>
);
}
}
export default reduxForm({
form: 'wizard',
validate
})(connect(mapStateToProps, mapDispatchToProps)(Topic));
I am using redux form.
this is what happening
What you're seeing occurs because both Topic instances are, technically, the same "form". When you call reduxForm(), you pass in a form name (wizard). This acts as the form's "identifier" and the form's data is stored in redux using this identifier. In other words, when you have two instances of your component being rendered, both instances are sharing the same exact form state in redux.
The way to solve this really depends on your business requirements. Are you rendering multiple Topics at the same time? If so, then you probably want to look into FieldArray or FormSection. If not, then the state from your first Topic form probably just isn't being destroyed before the second Topic form is displayed.
Related
I'm working my practice app using ReactJs and Django. My problem is when I update the Data via Axios "onChange" event not working.
updateForm.js
class Contact extends Component {
constructor(props) {
super(props);
this.state = {
Name: '',
Contact: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleformsubmit = this.handleformsubmit.bind(this);
}
handleformsubmit = (event, requestType, id) => {
const Name = event.target.elements.Name.value;
const Contact = event.target.elements.Contact.value;
switch (requestType) {
case 'put':
return axios.put(`http://127.0.0.1:8000/Contact/${id}/`, {
Name: Name,
Contact: Contact
})
.then(res => console.log(res))
.catch(error => console.log(error));
}
}
handleChange(event) {
this.setState({ Name: event.target.value });
this.setState({ Contact: event.target.value });
}
render() {
const Name = this.state.Item_no;
const Contact = this.state.Supplier;
return (
<Form onSubmit={(event) => this.handleformsubmit(
event,
this.props.requestType,
this.props.id)}>
<div className="container">
<GridContainer component="span">
<Card>
<CardHeader color="primary">
<h4>Update Contact Info</h4>
</CardHeader>
<CardBody>
<GridContainer>
<GridItem sm={12} md={6}>
<CustomInput
labelText="Name"
type="text"
id="Name"
name="Name"
formControlProps={{
fullWidth: true
}}
value={Name}
onChange={this.handleChange}
/>
</GridItem>
<GridItem sm={12} md={6}>
<CustomInput
labelText="Contact"
name="Contact"
type="text"
id="Contact"
formControlProps={{
fullWidth: true
}}
value={Contact}
onChange={this.handleChange}
/>
</GridItem>
I already done searching and tried the possible solution's but nothing works. Nothing wrong with "put" actions but on my onChange event not trigger when I update the data. Help me how to work this.Thank you!
From your code, I can see The onChange function just update the new state and the function handleformsubmit will only be called until you submit the form
It looks like your input value is controlled, which refuses any changes from handleChange
Try value={this.state.Name}
this is my React worksection.js file and its function made instead of class
export default function WorkSection() {
now i need to do here constructor to initialise state and do function operations which i'll call on button click
return (
<div className={classes.section}>
<GridContainer justify="center">
<GridItem cs={12} sm={12} md={8}>
<h2 className={classes.title}>Work with us</h2>
<h4 className={classes.description}>
BAIOS BAY LLP is looking for collaborations with you, Contact us
today !
</h4>
<form onSubmit={this.handleSubmit}>
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="Your Name"
id="name"
onChange={this.handleChange}
defaultValue={this.state.name}
formControlProps={{
fullWidth: true
}}
/> </GridItem> </GridContainer>
</div>
);
}
this is my form where i am submitting name and will add button click so how can i initialise state and functions to call onclick functions where
my functions are as :
constructor(props) {
super(props);
this.state = {
name : ''}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
handleClick = event => {
this.setState({
[event.target.id]: event.target.checked
});
}
handleSubmit = event => {
event.preventDefault();
if (this.state.username === '') {
this.showMessage('Info', "Username is empty");
return;
}
}
i need to place this function and i did it with class worksection but how to do it with export default function Worksection()
The thing you're probably looking for called react hooks. They allow you to use state management in your functional components. They're cool because they're lightweight in compare with class components.
First, import useState function from react:
import { useState } from 'react'
Then, before your return, add these lines:
const [name, setName] = useState('');
The first argument here is the name of your state property, and the second one is the function to change it.
So, instead of this:
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
Write this:
handleChange = event => {
setName(event.target.value);
}
If you want to make it more complex, you can rewrite your hook from this:
const [name, setName] = useState('');
to this:
const [state, setState] = useState({
name: '',
checked: false,
});
export default function WorkSection() {
const { register, handleSubmit } = useForm();
const onSubmit = data => {
axios
.get("
...................... my code ...............
and my input form is :
return (
<div className={classes.section}>
<form onSubmit={handleSubmit(onSubmit)}>
<Input
name="Name"
placeholder="Name"
inputRef={register}
fullWidth={true}
/>
<Input
name="email"
type="email"
placeholder="Email"
fullWidth={true}
inputRef={register({ required: true })}
/>
<Input
name="contact"
placeholder="Contact"
fullWidth={true}
inputRef={register({ required: true })}
/>
<Input
name="description"
placeholder="Message"
multiline={true}
fullWidth={true}
inputRef={register({ required: true })}
/>
<button className="btnColor" justify="center" type="submit">
Send Message
</button>
</GridItem>
</GridContainer>
</form>
</div>
Basically i used
inputRef={register}
and other part as satated above.
Right code , worked for me ~
I have a problem passing value to handleChange during formik validation.So I created a component whose quantity is added depending on the number of numChild. And I would like when he clicks the icon he can add any number of skills to the skills table.Can someone guide me how to do it correctly? Thanks for the tips
//Parent component
state = {
numChildren: 0 //
};
addComponent = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
};
render()
{
const children = []; // in loop i try created components
//in this place i try set props to child component but i do something wrong and i get error
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<SkillComponent key={i} name1={values.skills.basicAttack.name}
name2={values.skills.specialAttack.name}
damage={values.skills.damage}
handleChange={handleChange}/>);
}
return(
<Formik
initialValues={{
name: '',
// here I try to add as many elements as the user creates skills by SkillComponent with this structure
/*
this my single skill
{
basicAttack: {
name: ''
},
specialAttack: {
name: ''
},
damage: 0
}
*/
skills: [
{
basicAttack: {
name: ''
},
specialAttack: {
name: ''
},
damage: 0
}
]
}}
validationSchema={validationSchema}
onSubmit={(values) => {
console.log("Working")
}}
/>
{({
values
handleChange,
handleBlur,
handleSubmit,
isSubmitting
}) => (
<Form onSubmit={handleSubmit}>
//some code
...
<FontAwesomeIcon
icon={faPlus}
onClick={this.addComponent}// If the user clicks I will increase the numChild
/>
{children}
</Form>
)
</Formik>
And child component
class SkillComponent extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<Form.Group controlId='formBasicEmail'>
<Form.Control
type='text'
name='name'
value={this.props.name1}
handleChage={this.props.handleChange}
/>
</Form.Group>
<Form.Group controlId='formBasicEmail'>
<Form.Control
type='text'
name='name'
value={this.props.name2}
handleChage={this.props.handleChange}
/>
</Form.Group>
<Form.Group controlId='formBasicEmail'>
<Form.Control
type='text'
name='name'
value={this.props.damage}
handleChage={this.props.handleChange}
/>
</Form.Group>
</div>
);
}
}
export default SkillComponent;
Thanks for all the help
First of all you're trying to access values variable in for loop which is not accessible there. In your case I would start with some generateChildren method which will be responsible for repeating your children components:
getChildren = (values, handleChange) =>
[...Array(this.state.numChildren)].map(num =>
<SkillComponent
key={num}
name1={values.skills.basicAttack.name}
name2={values.skills.specialAttack.name}
damage={values.skills.damage}
handleChange={handleChange}
/>
)
render() {
<Formik>
{({ values, handleChange, handleBlur, handleSubmit, isSubmitting }) => (
<Form onSubmit={handleSubmit}>
<FontAwesomeIcon icon={faPlus} onClick={this.addComponent} />
{this.getChildren(values, handleChange)}
</Form>
)}
</Formik>
}
I have a form that has 10+ input fields that update the state of the class. To make things look cleaner I moved all input fields with labels into a separate component so I could re-use it for each input instead. This component takes 2 parameters and serves as a child in my main class.
child component:
const Input = ({ name, placeholder }) => {
return (
<div className="wrapper">
<Row className="at_centre">
<Col sm="2" style={{ marginTop: "0.5%" }}><Form.Label>{ name }</Form.Label></Col>
<Col sm="5"><Form.Control placeholder={ placeholder }/></Col>
</Row>
</div>
)
}
parent:
state = { name: '', description: '' }
handleSubmit = (e) => {
e.preventDefault()
console.log(this.state);
}
render(){
return(
<Form style={{ marginBottom: "5%", padding: 10 }} onSubmit={ this.handleSubmit } >
<Input name="Name: " placeholder="How is it called?" onChange={ (event) => this.setState({name: event.target.value}) }/>
<Input name="Description: " placeholder="Please describe how does it look like?" onChange={ (event) => this.setState({description: event.target.value}) }/>
<Button variant="outline-success" size="lg" type="submit" >SUBMIT</Button>
</Form>
)
}
After I did that I can't find the way how to update the state from my child components when the text is changed. All my attempts to do so either crashed the website or did nothing. I am still new to React.js so any feedback is appreciated.
Pass onChange event to your child component and wire it with Form.Control control.
Your Input component will be,
const Input = ({ name, placeholder, onChange }) => {
return (
<div className="wrapper">
<Row className="at_centre">
<Col sm="2" style={{ marginTop: "0.5%" }}>
<Form.Label>{name}</Form.Label>
</Col>
<Col sm="5">
<Form.Control onChange={onChange} placeholder={placeholder} />
</Col>
</Row>
</div>
);
};
And your Parent component is,
class Parent extends React.Component {
state = { name: "", description: "" };
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
render() {
return (
<Form
style={{ marginBottom: "5%", padding: 10 }}
onSubmit={this.handleSubmit}
>
<Input
name="Name: "
placeholder="How is it called?"
onChange={event => this.setState({ name: event.target.value })}
/>
<Input
name="Description: "
placeholder="Please describe how does it look like?"
onChange={event => this.setState({ description: event.target.value })}
/>
<Button variant="outline-success" size="lg" type="submit">
SUBMIT
</Button>
</Form>
);
}
}
Working Codesandbox here.
In React, properties flow from the parent component to the child component, so you cannot directly "pass" the state from the child to the parent.
What you can do however is to have the parent pass a callback function to the child that will be called to update the parent's state.
Here is an example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
}
updateName(name) {
if (name === this.state.name) return;
this.setState({ name });
}
render() {
return (
<div>
<p>The name is {this.state.name}</p>
<ChildComponent handleNameUpdate={name => this.updateName(name)} />
</div>
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
}
handleInputChange(e) {
this.setState({ name: e.target.value });
this.props.handleNameUpdate(e.target.value)
}
render() {
return <input type="text" value={this.state.name} onChange={e => this.handleInputChange(e)} />;
}
}
You have to build what is known as a controlled component.
const Input = ({ label, name, onChange, placeholder }) => (
<div className="wrapper">
<Row className="at_centre">
<Col sm="2" style={{ marginTop: "0.5%" }}>
<Form.Label>{ label }</Form.Label></Col>
<Col sm="5">
<Form.Control name={ name }
value={ value }
placeholder={ placeholder }
onChange={ onChange }
/>
</Col>
</Row>
</div>
)
And in your parent,
state = { name: '', description: '' }
handleChange = ({ target: { name, value } }) => this.setState({ [name]: value })
render() {
const { name, description } = this.state
<Form style={{ marginBottom: "5%", padding: 10 }} onSubmit={ this.handleSubmit } >
<Input label="Name: " name="name" value={name} onChange={handleChange}/>
<Input label="Description: " description="description" value={description} onChange={handleChange}/>
<Button variant="outline-success" size="lg" type="submit" >SUBMIT</Button>
</Form>
}
Advice
Try to avoid manufacturing lambda methods inside the render function as much as possible and have a class property as a lambda method so that lambdas do not need to be manufactured on every render cycle.
I am playing with React and trying to save the text that user type to the input to the state. I have added to the textarea an onChange attribute for setting the state.
However, when I start typing, I see error in the console stating TypeError: _this.setState is not a function.
I've tried different ways of trying to fix it, but still don't have it.
const NewItemForm = props => (
<Form onSubmit={props.send_form}>
<Form.Group>
<TextArea
placeholder='Name your first item here'
name='item_msg'
onChange={e => this.setState({ item_msg: e.target.value })} />
<Form.Button primary content='Create Item' />
</Form.Group>
</Form>
)
class App extends Component {
constructor () {
super();
this.state = {
item_msg: ''
}
}
handleSubmit(e){
e.preventDefault();
console.log(this.state.item_msg);
}
render() {
return (
<div className="App">
<MainHeaderr />
<Container>
<NewItemForm send_form={this.handleSubmit.bind(this)} />
</Container>
</div>
);
}
}
export default App;
Functional components do not have lifecycle methods and... state :)
const NewItemForm = props => (
<Form onSubmit={props.send_form}>
<Form.Group>
<TextArea
placeholder='Name your first item here'
name='item_msg'
onChange={e => this.setState({ item_msg: e.target.value })} />
<Form.Button primary content='Create Item' />
</Form.Group>
</Form>
)
This won't work:
onChange={e => this.setState({ item_msg: e.target.value })} />
What you need is to pass callback:
const NewItemForm = props => (
<Form onSubmit={props.send_form}>
<Form.Group>
<TextArea
placeholder='Name your first item here'
name='item_msg'
onChange={props.onInputChange} />
<Form.Button primary content='Create Item' />
</Form.Group>
</Form>
)
class App extends Component {
constructor () {
super();
this.state = {
item_msg: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleSubmit(e){
e.preventDefault();
console.log(this.state.item_msg);
}
handleInputChange(e) {
this.setState({ item_msg: e.target.value })
}
render() {
return (
<div className="App">
<MainHeaderr />
<Container>
<NewItemForm send_form={this.handleSubmit} onInputChange={this.handleInputChange} />
</Container>
</div>
);
}
}
I get where you are coming from, but NewItemForm will get transpiled to React Element so this will reference that Element, not the App component.
React without JSX
Functional components are stateless so you can't call setState within them. You can pass a callback from your parent component that sets state in the parent component as follows:
handleChange = e => this.setState({ item_msg: e.target.value });
<NewItemForm onChange={this.handleChange} />
And then in your NewItemForm component:
<TextArea
placeholder='Name your first item here'
name='item_msg'
onChange={props.onChange}
/>
NewItemForm is function component and function comopent does not have lifecycle method use class component.
You need to either use arrow function or bind the function in constructor like below
constructor(props) {
super(props);
this.state = { date: new Date() };
this.tick = this.tick.bind(this);
}
setInterval(()=>this.tick, 1000);
or Use arrow function
setInterval(()=>this.setState({
date: new Date(),
}), 1000);