var uuid = require('uuid-v4');
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true
var questionNum = 0;
class App extends Component {
constructor(props){
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
addQuestion = () => {
questionNum++;
this.setState({
questions: this.state.questions.concat(["question","hi"])
});
console.log(this.state.questions);
this.setState({
answers: this.state.answers.concat(["hello","hi"])
});
console.log(this.state.answers);
console.log(questionNum);
console.log(this.state.title);
console.log(this.state.author);
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title: <input type="text" value={this.state.title} onChange={this.handleChange} name="title" key={uuid()}/><br/>
Who's the Author? <input type="text" value={this.state.author} onChange={this.handleChange} name="author" key={uuid()}/><br/><br/>
</div>
<div className="questions">
Now let's add some questions... <br/>
{this.addQuestion}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;
Both of the inputs on my page unfocus after typing only one character. Here's my code, I'm not entirely sure where I am going wrong here, it all worked just fine yesterday.
If there is anything else that you need to know just leave a comment and I will get to it. Your help is much appreciated, thanks!
When the key given to a component is changed, the previous one is thrown away and a new component is mounted. You are creating a new key with uuid() every render, so each render a new input component is created.
Remove the key from your inputs and it will work as expected.
<div className="Intro">
Give your Quiz a title:
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
name="title"
/>
<br/>
Who's the Author?
<input
type="text"
value={this.state.author}
onChange={this.handleChange}
name="author"
/>
<br/><br/>
</div>
Tholle's response is correct. However, you should make some adjustments to how you interact with state. I've reworked your code with comments and included the uuid fix.
class App extends Component {
constructor(props){
super(props);
this.state = {
title: "",
author: "",
questions: [],
answers: []
}
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
}
handleChange({ target }) {
// since you'll always have a synthetic event just destructure the target
const { checked, name, type, value } = target;
const val = type === 'checkbox' ? checked : value;
this.setState({
[name]: val
});
}
addQuestion() {
// never modify state directly -> copy state and modify
const { answers, questions } = Object.assign({}, this.state);
// update your answers
answers.push(["hello", "hi"]);
// update your questions
questions.push(["question", "hi"]);
// now update state
this.setState({
answers,
questions
}, () => {
console.log(this.state);
});
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title: <input type="text" value={this.state.title} onChange={this.handleChange} name="title" /><br/>
Who's the Author? <input type="text" value={this.state.author} onChange={this.handleChange} name="author" /><br/><br/>
</div>
<div className="questions">
Now let's add some questions... <br/>
{this.addQuestion}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;
Related
UPDATE : There is a problem in the FormInput component, the state is changing but the new state passed as props to FormInput is not being reflected.
I was creating this contact form in react and I wanted it to clear after submission.
After rigorous searching on the web, I couldn't find why my this.setState is not clearing the input fields as I'm changing all the inputs to an empty string. Take a look:
class contactPage extends React.Component{
constructor(props){
super(props);
this.state = {
name :'',
email : '',
subject : '',
message : ''
}
}
handleChange = (e) =>{
const {name,value} = e.target;
this.setState({
[name] : value
})
}
handleSubmit = (e) => {
e.preventDefault();
this.setState({
name :'',
email : '',
subject : '',
message : ''
});
}
render(){
const { name,email,subject,message } = this.state;
return(
<div className='contact-page'>
<h1 className='title'> Contact Page </h1>
<div className='form-container'>
<form onSubmit={this.handleSubmit}>
<div className='input-container'>
<div className='form-input'>
<FormInput handleChange = {this.handleChange} value={name} type='text' name ='name' label='Full Name'/>
</div>
<div className='form-input'>
<FormInput handleChange = {this.handleChange} value={email} type='email' name ='email' label='Email'/>
</div>
<div className='form-input'>
<FormInput handleChange = {this.handleChange} value={subject} type='text' name ='subject' label='Subject'/>
</div>
<div className='form-input'>
<FormInput handleChange = {this.handleChange} value={message} name ='text-area' label='Message'/>
</div>
<div className='form-input'>
<CustomButton
type='submit' value='SEND' >SEND</CustomButton>
</div>
</div>
</form>
</div>
</div>)
}
}
I am not very confidant that the state isn't chaging, it might be a problem with your inputs
I've done just the same component, using native inputs instead, and it works perfectly
I have assigned default values to the input tag. When I try to submit without changing the values of the input tag, the value passed is undefined.
What I want is that if the input value is changed, to update the state value with the new value and if the input tag value is not changed, then to pass the default value to the state.
I am new to React.
import React, { Component } from 'react';
import Firebase from '../Firebase';
import _ from 'lodash';
import '../App.css';
class Modify extends Component {
constructor(props) {
super(props);
//state
this.state = {
personDetails: {},
detail: ''
};
//bind
this.renderData = this.renderData.bind(this);
this.handleChange = this.handleChange.bind(this);
this.saveEdit = this.saveEdit.bind(this);
}
//lifecycle
componentDidMount() {
Firebase.database().ref('/nannydetails').on('value', (snapshot) => {
this.setState({
personDetails: snapshot.val()
})
})
}
//handle change
handleChange(event) {
this.setState({
[event.target.name]: event.target.value.toLowerCase()
});
}
//render data
renderData() {
return _.map(this.state.personDetails, (personDetail, key) => {
return(
<div className="pt-2 pb-1 m-2 border border-dark bg-warning row" key={key}>
<div className="col-md-3 col-xs-3 text-right">
<img src={require('./profile.png')} alt="cam"/>
</div>
<div className="col-md-9 col-xs-9 p-2">
<div className="headline">
<h1>Full Name: </h1>
<input className="form-control" type="text" name="fullName" defaultValue={personDetail.fullname} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>Contact Number: </h1>
<input className="form-control" type="text" name="phone" defaultValue={personDetail.phone} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>Experience: </h1>
<input className="form-control" type="text" name="experience" defaultValue={personDetail.experience} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>City: </h1>
<input className="form-control" type="text" name="city" defaultValue={personDetail.city} onChange={this.handleChange}/>
</div>
<div className="headline">
<h1>State: </h1>
<input className="form-control" type="text" name="state" defaultValue={personDetail.state} onChange={this.handleChange}/>
</div>
<button className="btn btn-success" type="button" onClick={() => this.saveEdit(key)}><i className="fa fa-pencil-square-o"></i> Save Edit</button>
<button className="btn btn-danger" type="button" onClick={() => this.handleDelete(key)}><i className="fa fa-trash"></i> Delete</button>
</div>
</div>
)
});
}
//handle save edit
saveEdit(key) {
// Firebase.database().ref(`/nannydetails/${key}`).push({
// fullname: this.state.fullname,
// phone: this.state.phone,
// experience: this.state.experience,
// city: this.state.city,
// state: this.state.state
// });
console.log(this.state.fullname);
console.log(this.state.phone);
console.log(this.state.experience);
console.log(this.state.city);
console.log(this.state.state);
}
//handle delete
handleDelete(key) {
const user= Firebase.database().ref(`/nannydetails/${key}`);
user.remove();
}
render() {
return (
<div className="container mt-4">
{this.renderData()}
</div>
);
}
}
export default Modify;
Update the change handler to update/mutate the existing state instead of adding a new property(key), i.e. on this.state.personDetails.X vs. this.state.X
//handle change
handleChange(event) {
// update the personDetails object you set in state on mount
const newPersonDetails = { ...this.state.personDetails };
newPersonDetails[event.target.name] = event.target.value.toLowerCase();
this.setState({
personDetails: newPersonDetails,
});
}
Make this as a controlled component. Provide a initial state in you component and all the updated will done into the state & that will directly reflect into your component.
Initial State:
this.state = {
personDetails: {
fullname :'test'
},
detail: ''
};
Input Field
input className="form-control" type="text" name="fullName" value={this.state.personDetail.fullname} onChange={this.handleChange}
onChange is only triggered when the value of input changes. The defaultValue can be defined in the initial state, and the defaultValue is overwritten when the value of input changes.
The best way to approach is to have a main component take data from any api (firebase in your case) and second child component which will be responsible to take input as props and return input(default/modified) on form action(submit). That way if data changes you get modified data else the default one.
class FormComp extends React.Component {
constructor(props) {
super(props);
this.state = {
name: this.props.form.name,
};
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
submit(e) {
e.preventDefault();
console.log(this.state);
}
handleChange(e) {
this.setState({name: e.target.value});
}
handleChan
render() {
return (
<div>
<form onSubmit={this.submit}>
<input
type="text"
value={this.state.name}
onChange={this.handleChange}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
Refer to that fiddle for example. https://jsfiddle.net/papish19/ys6utv3k/7/
Ok so here's my code:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
var uuid = require("uuid-v4");
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true
var questionNum = 0;
class App extends Component {
constructor(props) {
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
};
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
this.removeItem = this.removeItem.bind(this)
}
componentDidMount() {
// componentDidMount() is a React lifecycle method
this.addQuestion();
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
removeItem (index) {
questionNum--;
this.setState(({ questions }) => {
const mQuestions = [ ...questions ]
mQuestions.splice(index, 1)
return { questions: mQuestions }
})
this.setState(({ answers }) => {
const mAnswers = [ ...answers]
mAnswers.splice(index, 4)
return { answers: mAnswers}
})
console.log(
"answers",
this.state.answers,
"questions",
this.state.questions,
questionNum,
this.state.title,
this.state.author
);
}
addQuestion() {
questionNum++;
this.setState(previousState => {
const questions = [
...previousState.questions,
<input
type="text"
onChange={this.handleChange}
name="question"
key={uuid()}
/>
];
const answers = [
...previousState.answers,
];
for (var i = 0; i < 4; i++) {
answers.push(
<input
type="checkbox"
name={uuid()}>
<input
type="text"
onChange={this.handleChange}
name={uuid()}
/>
</input>
);
}
return { questions, answers };
});
console.log(
"answers",
this.state.answers,
"questions",
this.state.questions,
questionNum,
this.state.title,
this.state.author
);
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 3.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div className="formDiv">
<form>
<div className="Intro">
Give your Quiz a title:{" "}
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
name="title"
/>
<br />
Who's the Author?{" "}
<input
type="text"
value={this.state.author}
onChange={this.handleChange}
name="author"
/>
<br />
<br />
</div>
<div className="questions">
<div className="questions">
Now let's add some questions... <br />
<ol>
{this.state.questions.map(question => {
return (
<li>
<div key={uuid()}>
Question
{question}<br />
Answer Choices<br />
{Array.from({ length: 4 }, () => (
<input type="text" key={uuid()} onChange={this.handleChange} />
))}
</div>
</li>
);
})}
</ol>
</div>
{
// This is what it would look like for the structure
// I proposed earlier.
// this.state.questions.map((question) {
// return (
// <div>{question.quesion}</div>
// {
// question.answers.map((answer) => {
// return (<div>{answer}</div>);
// })
// }
// );
// })
// This would output all questions and answers.
}
</div>
</form>
<button id="addQuestionButton" onClick={this.addQuestion}>Add Question</button>
{ this.state.questions.map((question, index) => {
return <button key={uuid()} onClick={ () => this.removeItem(index) }>Remove Question</button>
}) }
</div>
</div>
);
}
}
export default App;
Ok so here's a link to a quick video demonstrating what it does as of now. In the video you can see the Remove Question buttons that are created (at the bottom of the form) each time a question is added. I would like to have each question's Remove Question button be next to it/in the same div. I'm not entirely sure how I would go about doing this. Any thoughts?
UPDATE: Ok so I have put the buttons inside of the same div with the actual question, but I realized that i am adding a button for each object in the array. Which means that when a question is added a button to remove it is added to every question on the form. I need to make it so it does not .map this. I'm not entirely sure what other function I will do for this, maybe I don't even need a function. I will try my best to work it out. Here's the updated code (some of it):
<div className="questions">
Now let's add some questions... <br />
<ol>
{this.state.questions.map(question => {
return (
<li>
<div key={uuid()}>
Question
{question}<br />
{
this.state.questions.map((question, index) => {
return <button key={uuid()} onClick={ () => this.removeItem(index) }>Remove Question</button>
})
}
Answer Choices<br />
{Array.from({ length: 4 }, () => (
<div>
<input type="checkbox" />
<input type="text" key={uuid()} onChange={this.handleChange} />
</div>
))}
</div>
</li>
);
})}
</ol>
</div>
Something like this...
<ol>
{this.state.questions.map((question, index) => {
return (
<li>
<div key={uuid()}>
Question
{question}<br />
<button onClick={ () => this.removeItem(index) }>
Remove Question
</button>
Answer Choices<br />
{Array.from({ length: 4 }, () => (
<div key={uuid()}>
<input type="checkbox" />
<input type="text" onChange={this.handleChange} />
</div>
))}
</div>
</li>
);
})}
</ol>
I'm having difficulty understanding how to handle radio buttons in forms with other types of inputs in React. I'm working from an example that incorporates string type input fields and another for pure radio buttons, but not sure how to get them to play nice together. The text field inputs work fine, but I can't get the radio button feature to work.
The basic theory behind the radio button of the React portion of this form is the checkbox for each option is set when a user clicks on a button and the checked value for that particular button is set to true via a logic check, while the other radio buttons are set to false.
I'm not sure how to integrate the handling of the input fields with the radio buttons.
Any guidance on this would be great!
Here's my form jsx:
class Project_form extends React.Component {
handleChange(e) {
const name = e.target.name;
const obj = {};
obj[name] = e.target.value;
this.props.onUserInput(obj);
}
handleOptionChange(e) {
const name = e.target.id;
const obj = {};
obj[name] = e.target.checked;
this.props.onChangeInput(obj);
}
handleSubmit(e) {
e.preventDefault();
this.props.onFormSubmit();
}
render() {
return (
<form onSubmit={(event) => this.handleSubmit(event)} >
<div className="form-group">
<input
id="project_name"
className="form-control"
type="text"
name="project_name"
placeholder="Enter Your Project Name"
value={this.props.project_name}
onChange={(event) => this.handleChange(event)} />
</div>
<div className="form-group">
<input
id="project_zipcode"
className="form-control"
type="text"
name="project_zipcode"
placeholder="Zipcode"
value={this.props.project_zipcode}
onChange={(event) => this.handleChange(event)} />
</div>
<div className="form-group">
<label>How Soon Do You Want this Project Started?</label>
<div className="radio">
<p><input type="radio"
value="1-2 Weeks"
name="project_timeframe"
id="project_timeframe_1-2_weeks"
checked={this.props.project_timeframe === 'ASAP'}
onChange={(event) => this.handleOptionChange(event)} />
<label>As Soon As Possible</label></p>
<p><input type="radio"
value="2-4 Weeks"
name="project_timeframe"
id="project_timeframe_2-4_weeks"
checked={this.props.project_timeframe === '2-4 Weeks'}
onChange={(event) => this.handleOptionChange(event)} />
<label>2-4 Weeks</label></p>
<p><input type="radio"
value="4-6 Weeks"
name="project_timeframe"
id="project_timeframe_4-6_weeks"
checked={this.props.project_timeframe === '4-6 Weeks'}
onChange={(event) => this.handleOptionChange(event)} />
<label>4-6 Weeks</label></p>
<p><input type="radio"
value="More Than 6 Weeks"
name="project_timeframe"
id="project_timeframe_more_than_6_weeks"
checked={this.props.project_timeframe === 'More Than 6 Weeks'}
onChange={(event) => this.handleOptionChange(event)} />
<label>More Than 6 Weeks</label></p>
</div>
</div>
<div className="form-group">
<input
type="submit"
value="Create Project"
className="btn btn-primary btn-lg"
/>
</div>
</form>
)
}
}
Here's my main component, which sets the state of the app.
class Projects extends React.Component {
constructor(props) {
super(props)
this.state = {
projects: this.props.projects,
project_name: '',
project_zipcode: '',
selectedTimeframeOption: 'ASAP'
}
}
handleUserInput(obj) {
this.setState(obj);
}
handleChangeInput(obj) {
this.setState({
selectedOption: obj.target.value
});
}
handleFormSubmit() {
const project = {
name: this.state.project_name,
zipcode: this.state.project_zipcode,
timeframe: this.state.project_timeframe
};
$.post('/projects',
{project: project})
.done((data) => {
this.addNewProject(data);
});
}
addNewProject(project){
const projects = update(this.state.projects, { $push: [project]});
this.setState({
projects: projects.sort(function(a,b){
return new Date(a['updated_at']) - new Date(b['updated_at']);
})
});
}
render() {
return (
<div>
<h2>Start a New Project</h2>
<a href="/projects/new/"
className="btn btn-large btn-success">Create a New Project</a>
{/* <%= link_to "Create a New Project", new_project_path, class: "btn btn-large btn-success" %> */}
<Project_form
project_name={this.state.project_name}
project_zipcode={this.state.project_zipcode}
project_timeframe={this.state.selectedTimeframeOption}
onUserInput={(obj) => this.handleUserInput(obj)}
onFormSubmit={() => this.handleFormSubmit()} />
{/* <% if #current_user.projects.any? %> */}
<div className="col-md-12">
<h3>Existing Projects</h3>
<div className="table-responsive">
<Project_list projects={this.state.projects} />
</div>
</div>
</div>
)
}
}
Ok, figured it out. Had to play around with getting the event object into the stateful component.
My form file now has this event handler added:
handleOptionChange(e) {
const option = e.target.name;
const obj = {};
obj[option] = e.target.value;
this.props.onUserInput(obj);
}
Then this.props.onUserInput(obj); gets called by the existing method in the Project.jsx file and I got rid of the handleChangeInput method.
handleUserInput(obj) {
this.setState(obj);
}
The state then flows down to the Project_form Component here:
<Project_form
project_name={this.state.project_name}
project_zipcode={this.state.project_zipcode}
project_timeframe={this.state.project_timeframe}
onUserInput={(obj) => this.handleUserInput(obj)}
onFormSubmit={() => this.handleFormSubmit()} />
I am facing trouble with initializing my form with redux-form and the 'initialValues' props. I read a lot posts (for instance here) and I don't manage to get it work...
I see that my initialValues is properly set but my fields are not updated with it... Maybe the issue is in my renderBasicField function but I don't know how to fix this.
Otherwise it could also be something like the prop is not populated yet when the component is rendered... But I don't know what to do to make it work as I must rely on mapStateToProps to feed it.
I saw a lot of post about this kind of issues and unfortunately for me I already set up the enableReinitialize property to true :)
Here is my code :
// My component
class ProfileForm extends React.Component {
render () {
console.log(this.props);
const { handleSubmit } = this.props;
const messageClassname = this.props.errorMessage !== undefined ? stylesShared.errorMessage : this.props.confirmationMessage !== undefined ? stylesShared.confirmationMessage : '';
return (
<div>
<div>
<div>
<form onSubmit={handleSubmit(this.props.onSubmitProfileUpdate)}>
<div>
<h4>Votre profil</h4>
</div>
<div className={messageClassname}>
{this.props.errorMessage &&
<span>{this.props.errorMessage}</span>
}
{this.props.confirmationMessage &&
<span>{this.props.confirmationMessage}</span>
}
</div>
<div>
<Field name='firstname' type='text' label='Prénom' component={renderBasicField} />
</div>
<div>
<Field name='lastname' type='text' label='Nom' component={renderBasicField} />
</div>
<div>
<Field name='email' type='email' label='Email' addon='#' component={renderBasicField} />
</div>
<div>
<Field name='telephone' type='text' label='Téléphone' component={renderBasicField} />
</div>
<div>
<Field name='ranking' className='input-row form-group form-control' options={this.getTennisRankingsOptions()} type='select' component={renderSelectField} />
</div>
<div>
<Field name='city' type='text' label='Ville' component={renderBasicField} />
</div>
<div>
<button className='btn btn-info btn-lg center-block' type='submit'>Mettre à jour</button>
</div>
</form>
</div>
</div>
</div>
);
}
}
const reduxFormDecorator = reduxForm({
form: 'profile',
enableReinitialize: true,
validate: validateProfileForm
});
const mapStateToProps = (state) => {
return {
initialValues: state.userConnection.loadProfile.user
};
};
const reduxConnector = connect(
mapStateToProps,
null
);
export default reduxConnector(reduxFormDecorator(ProfileForm));
And the code to render my field :
// My renderFunction
export const renderBasicField = ({input, meta: {touched, error}, label, type='text', id, addon, styleClasses, handleChange, controlledAsyncValue}) => {
const inputStyles = getInputStyles(input.value, touched, error);
if (controlledAsyncValue !== input.value) {
input.value = controlledAsyncValue;
input.onChange(input.value);
}
return (<div className={inputStyles.container}>
{displayInputLabel(inputStyles.input.idInput, label)}
<div className={addon && 'input-group'}>
{addon && <span className='input-group-addon'>{addon}</span>}
<input
{...input}
className={classNames(styles.basicInputField, styleClasses)}
id={id}
value={input.disabled ? '' : input.value}
onChange={getOnChangeAction(input.onChange, handleChange)}
placeholder={label}
type={type}
aria-describedby={inputStyles.input.ariaDescribedBy}
/>
</div>
{touched && error &&
displayErrorMessage(error)}
</div>);
};
I am wondering if I am ignoring the initialValue with my custom renderBasicField function but in that case, I would I retrieve this value to set my input ?
Thanks a lot for your help ! :)
Try to switch connect and form decorator. It should helps.
export default reduxFormDecorator(reduxConnector(ProfileForm));