react.js - how to add element on success in dynamically generated button? - javascript

I have dynamically generated component with textfields and buttons. Each button do the ajax request. It's all working fine. However, I want to display the success message or error message on button itself, adding some icon on it. This is where I got stuck. I setup the flag and change the state, but it will change on all the buttons as expected. I also tried to change the current target, but the reference didn't work in success callback. Can someone please help me with this.
const FormGroup = ({index, type, field, value, onChange, spinner, isLoading, error, buttonType, brandList, handleBrandConfiguration, checkAvailability, handleCaseType, options, handlerRemoveItem})=> {
return(
<div>
<div className="form-group">
<label>{index}</label>
<input type="text"
name={field}
className="form-control"
value={value}
onChange={onChange}
/>
<select className="form-control" defaultValue="" onChange={handleBrandConfiguration}>
<option value="">Please select brand</option>
{brandList}
</select>
<select className="form-control" defaultValue="" onChange={handleCaseType}>
<option value="">Please select case template</option>
{options}
</select>
<button
type={buttonType}
className={classname(isLoading ? error ? "button button-danger" : "button button-success" : error ? "button button-danger" : "button button-primary")}
onClick={checkAvailability}>
<i className={classname(spinner ? error ? '': "fa fa-spinner fa-spin": '')}></i> {isLoading ? error ? 'Not Found' :<i className="fa fa-check fa-2" aria-hidden="true"></i> : error ? 'Not Found': 'Check Availability'}</button>
<input
type="button"
className="button button-danger"
value="Remove Item"
onClick={handlerRemoveItem}/>
</div>
</div>
);
};
Thanks

If you move your checkAvailability and result of api request to FormControl component you can set error and success for single component.
For Example:
class FormGroup extends React.Component{
constructor(props) {
super(props);
this.state = {
availabilityResult: null
};
}
callApi(username){
return(axios.get('https://api.github.com/users/' + username));
}
onChange(e){
this.setState({
itemCode: e.target.value
});
}
checkAvailability(e){
const username = this.state.itemCode;
let currentValue = e.currentTarget.value;
//ajax call goes here
this.callApi(username).then(response => {
const login = response.data.login;
this.setState({
availabilityResult: (login === 'mesmerize86')
});
}).catch(err => {
console.log(err);
});
}
render() {
const {index, field, value, handleRemoveItem} = this.props;
let inputState = '';
if (this.state.availabilityResult === true) inputState = 'button-success';
else if (this.state.availabilityResult === false) inputState = 'button-danger';
return(
<div className="form form-inline">
<div className="form-group">
<label>{index}</label>
<input type="text"
name={field}
className="form-control"
value={value}
onChange={this.onChange.bind(this)}
/>
<input
type="button"
className={`button button-primary ${inputState}`}
value="Check Availability"
onClick={this.checkAvailability.bind(this)} />
<input
type="button"
className="button button-danger"
value="Remove Item"/>
</div>
</div>
)
}
}
class Main extends React.Component {
constructor(props){
super(props);
this.state = {
rowList : [],
itemCodes: [],
itemCode: ''
}
}
handlerAddRow(){
const rowList = this.state.rowList.concat(FormGroup);
this.setState({ rowList });
}
handleRemoveItem(){
console.log('remove Item');
}
render(){
const rowList = this.state.rowList.map((row, index) => {
return (<FormGroup key={index} index={index+1} field={`"itemCode_"${index}`} />);
});
return(
<div className="container">
<input type="button" value="Add a row" className="button button-primary" onClick={this.handlerAddRow.bind(this)} /> <i class="fa fa-spinner" aria-hidden="true"></i>
{rowList}
</div>
);
}
}
ReactDOM.render(<Main />, document.getElementById('app'));

Related

React form does not render the state values I put on constructor

I have a tab component with 2 tabs. The first tab contains a list of employees and a button that transfers you in the second tab (contact form) containing the data of the selected employee. In the first tab I create an object of an employee, send it in the second tab and in the second tab i set the state.name, state.surname with the object values.
The problem is that in order to load the data in the form I need to change back in the first tab and go to the second tab again.
The tabs component
import React from 'react';
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box p={3}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired,
};
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
var importErg = new Boolean(false);
export function getImport(){
return importErg;
}
export const globalErg = {
onoma: "",
epitheto: ""
}
export function getGlobalErg(){
return globalErg;
}
async function getErgByeID(ErgEid){
globalErg.onoma = ""
globalErg.epitheto = ""
await fetch(URL+"/ergazomenoi?eid=eq."+ErgEid)
.then(response => {if(response.ok){
return response.json()
}
else {
alert('Something went wrong')
throw new Error('Something went wrong');
}
})
.then(data => {
globalErg.onoma = data[0].onoma
globalErg.epitheto = data[0].epitheto
}
)
}
export default function SimpleTabs() {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
function more(ergID){
setValue(1);
getErgByeID(ergID);
}
}
return (
<div className="main">
<AppBar position="static" id = "topbar">
<Tabs value={value} onChange={handleChange}>
<Tab label="Employees" {...a11yProps(0)}/>
<Tab label="Contact" {...a11yProps(1)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
<Table />
<Button style={{color: "#fff", background: "#111", marginRight: "2.5px", marginLeft:"2.5px", marginTop:"5px"}} onClick={() => more()}>
Contact
</Button>
</TabPanel>
<TabPanel value={value} index={1} id = 'tab'>
<Contact/>
</TabPanel>
</div>
);
}
the form component
import React, { Component, useEffect } from "react";
import {getGlobalErg} from "./Proswpiko-tabs";
class Personal_info extends Component {
constructor(props){
super(props);
let erg = getGlobalErg();
this.state = {
onoma: erg.onoma,
epitheto: erg.epitheto,
};
}
onomaChangeHandler = (event) => {
this.setState({onoma: event.target.value});
}
epithetoChangeHandler = (event) => {
this.setState({epitheto: event.target.value});
}
render() {
return (
<form onSubmit = {this.SubmitHandler}>
<div >
<p id = "topText" align = "center">
<h2>Contact info</h2>
</p>
<img id="top" src="top.png" alt=""></img>
<div id="form_container">
<form id="form" class="appnitro" method="post" action="">
<div class="form_description">
<h2>Personal info</h2>
</div>
<ul>
<li id = "li_3">
<label class="description" for="element_3" >Όνομα </label>
<span>
<input type ="text" id="nameInput" name= "nameInput" class="element text" maxLength="255" size="15" onChange={this.onomaChangeHandler} value = {this.state.onoma} required/>
<label>Name</label>
</span>
<span>
<input type ="text" id="surNameInput" name= "surNameInput" class="element text" maxLength="255" size="14" onChange={this.epithetoChangeHandler} value = {this.state.epitheto} required/>
<label>Surname</label>
</span>
</li>
</ul>
</form>
<div id="footer">
</div>
</div>
<img id="bottom" src="bottom.png" alt=""></img>
</div>
</form>
);}}
export default Personal_info;
It seems that since the setState func is async It didnt had time to fill the imput boxes.
Solution: Added setTimeout and now everything works fine

How to pass the default value back as input value if you don't want the value to be changed?

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/

Deleting Dynamic Element Reactjs

--FieldSection.js--
import React, { Component } from 'react';
import Field from './Field.js';
class FieldSection extends Component{
constructor(props){
super(props);
this.state ={
numberOfFields: 1
}
}
addField = () => {
const { numberOfFields } = this.state;
this.setState({ numberOfFields: numberOfFields + 1 });
}
listFields = (numberOfFields) => {
var fields = [];
for(var i=0; i<numberOfFields; i++){
fields.push(
(<Field number={i} />)
)
}
return fields;
}
render () {
const {listFields, addField} = this;
const {numberOfFields} = this.state;
return (
<div>
<label><u>Fields</u></label>
{listFields(numberOfFields)}
<div id="fieldButtons">
<button id="addField" type="button" onClick={addField}> Add Field </button>
<button id="removeField" type="button"> Remove Field </button>
</div>
</div>
)
}
}
export default FieldSection;
-----------------Field.js-------------------
import React from 'react';
class Field extends React.Component {
constructor(props){
super(props);
this.state = {
value: 'empty',
specVisible: 'hidden',
display: 'none'
};
}
SelectChange = (event) => {
this.setState({value: event.target.value});
if(event.target.value === "string" )
{
this.setState({specVisible: 'visible'});
this.setState({display: 'block'});
}
else {
this.setState({specVisible: 'hidden'})
this.setState({display: 'none'})
}
}
render (){
const {SelectChange} = this;
const {value, specVisible, display} = this.state;
return (
<div>
<div>
<label><strong>New Field </strong></label>
<div id="remove-" className="remove" style={{display: "inline", visibility: "hidden"}}>
<label> --Remove </label> <input type="checkbox" id="removeBox" className="rmvCheckbox" />
<br />
</div>
<label> Name: </label>
<input id="name-" className="name" type="text" name="name" /> <br />
<label> Description: </label>
<input id="description-" className="description" name="description" /> <br />
<label> Datatype: </label>
<select value={value} onChange={SelectChange} id={`selectData-${this.props.number}`} className="selectData" name="selectData" /*onClick={AddListener}*/>
<option value="empty"> </option>
<option value="string"> String </option>
<option value="character"> Character </option>
<option value="timestamp"> Timestamp </option>
<option value="integer"> Integer </option>
<option value="long"> Long </option>
<option value="double"> Double </option>
<option value="boolean"> Boolean </option>
</select> <br />
</div>
<div id={`specifySection-${this.props.number}`} className="specifySection" style={{visibility: specVisible, display: display}} >
<label> Specify Length: </label>
<input className="specifyLength" type="text" name="length"/> <br />
</div>
</div>
)}
}
export default Field
I am trying to implement a feature where you click "Remove Field" button and a list of checkboxes next to all the new fields appears. Whenever you confirm the deletion, it would delete all the elements that are selected.
Obviously I could subtract one to the numberOfFields state, however I want to delete specific elements, rather than just the last field.
What would that look like?
You can do it as follows. The basic idea is to get all the ids of the fields that need to be deleted and iterate over them and delete all the components corresponding to these ids.
Sandbox for code
When addField is called fields state of FieldsSelection
component is updated by adding a key with unique id, with the Field
component as its value along with all the props.
The remove state tracks if remove Field has been clicked and
toggles the remove checkbox in each Field component by passing it
as a prop.
fieldsToRemove state is updated via the markFields prop in
Field component each time a remove field checkbox is clicked.
When deleteFields is called, it iterates over the fieldsToRemove
state and removes the corresponding components from the fields
state object.
I used uuid package for unique ids for each Field as opposed to deleting
via index, which is not a great way and also conflicts with the key prop of
react.
FieldSection.js
import React, { Component } from "react";
import Field from "./Field.js";
import { v4 } from "uuid";
class FieldSection extends Component {
constructor(props) {
super(props);
this.state = {
fields: {},
remove: false,
fieldsToRemove: []
};
}
addField = () => {
const fields = this.state.fields;
const id = v4();
fields[id] = <Field key={id} id={id} mark={this.markFields} />;
this.setState({ fields });
};
listFields = () => {
var ids = Object.keys(this.state.fields);
return ids.map(id => {
return React.cloneElement(this.state.fields[id], {
remove: this.state.remove
});
});
};
markFields = (checked, i) => {
if (checked) {
const arr = [...this.state.fieldsToRemove];
arr.push(i);
this.setState({ fieldsToRemove: arr });
} else {
const arr = this.state.fieldsToRemove.filter(x => i !== x);
this.setState({ fieldsToRemove: arr });
}
};
removeFields = () => {
this.setState({ remove: !this.state.remove });
};
deleteFields = () => {
const fields = { ...this.state.fields };
this.state.fieldsToRemove.forEach(id => {
delete fields[id];
});
this.setState({ fields, fieldsToRemove: [], remove: false });
};
render() {
const { listFields, addField, removeFields, deleteFields } = this;
const { numberOfFields, remove } = this.state;
return (
<div>
<label>
<u>Fields</u>
</label>
{listFields()}
<div id="fieldButtons">
<button id="addField" type="button" onClick={addField}>
{" "}
Add Field{" "}
</button>
<button id="removeField" type="button" onClick={removeFields}>
{" "}
Remove Field{" "}
</button>
<br />
<button type="button" onClick={deleteFields}>
{" "}
Delete Fields{" "}
</button>
</div>
</div>
);
}
}
export default FieldSection;
Field.js
import React from "react";
class Field extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "empty",
specVisible: "hidden",
display: "none"
};
}
SelectChange = event => {
this.setState({ value: event.target.value });
if (event.target.value === "string") {
this.setState({ specVisible: "visible" });
this.setState({ display: "block" });
} else {
this.setState({ specVisible: "hidden" });
this.setState({ display: "none" });
}
};
render() {
const { SelectChange } = this;
const { value, specVisible, display } = this.state;
const styles = this.props.remove
? { display: "inline", visibility: "visible" }
: { display: "inline", visibility: "hidden" };
return (
<div>
<div>
<label>
<strong>New Field </strong>
</label>
<div id="remove-" className="remove" style={styles}>
<label> --Remove </label>{" "}
<input
type="checkbox"
id="removeBox"
className="rmvCheckbox"
onChange={e => {
this.props.mark(e.target.checked, this.props.id);
}}
/>
<br />
</div>
<label> Name: </label>
<input id="name-" className="name" type="text" name="name" /> <br />
<label> Description: </label>
<input
id="description-"
className="description"
name="description"
/>{" "}
<br />
<label> Datatype: </label>
<select
value={value}
onChange={SelectChange}
id={`selectData-${this.props.number}`}
className="selectData"
name="selectData" /*onClick={AddListener}*/
>
<option value="empty"> </option>
<option value="string"> String </option>
<option value="character"> Character </option>
<option value="timestamp"> Timestamp </option>
<option value="integer"> Integer </option>
<option value="long"> Long </option>
<option value="double"> Double </option>
<option value="boolean"> Boolean </option>
</select>{" "}
<br />
</div>
<div
id={`specifySection-${this.props.number}`}
className="specifySection"
style={{ visibility: specVisible, display: display }}
>
<label> Specify Length: </label>
<input className="specifyLength" type="text" name="length" /> <br />
</div>
</div>
);
}
}
export default Field;

Getting Select Value Using React

Trying to access Bootstraps select form value using React props. This is what i've tried so far but it doesn't store the options value.
Example Code:
class BottomBar extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden: true,
items: [],
text: '',
priority: ''
};
this.handleClick = this.handleClick.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handlePriority = this.handlePriority.bind(this);
};
handleChange(e) {
this.setState({text: e.target.value});
}
handlePriority(t) {
this.setState({priority: t.target.value});
}
handleSubmit(e) {
e.preventDefault();
if(!this.state.text.length) {
return;
}
const newItem = {
text: this.state.text,
id: Date.now(),
priority: this.state.priority
};
this.setState(state => ({items: state.items.concat(newItem), text: '', priority: ''}));
console.log(newItem);
}
handleClick(t) {
i++;
if(i === 1) {
this.setState({hidden: false});
} else if(i === 2) {
this.setState({hidden: true});
i = 0;
}
}
render() {
const { classes } = this.props;
return(
<React.Fragment>
<AddCard items={this.state.items} id={this.state.id} priority={this.state.priority} item={this.state.item}/>
<CssBaseline />
<AppBar position="fixed" color="primary" className={classes.appBar}>
{!this.state.hidden ? <TodoList text={this.state.text} handlePriorty={this.handlePriorty} priority={this.state.priority} handleSubmit={this.handleSubmit} items={this.state.items} handleChange={this.handleChange}/> : null}
<Toolbar className={classes.toolbar}>
<IconButton color="inherit" aria-label="Open drawer">
<MenuIcon />
</IconButton>
<a href="#" onClick={this.handleClick}>
<Button variant="fab" color="secondary" aria-label="Add" className={classes.fabButton}>
<AddIcon />
</Button>
</a>
<div>
<IconButton color="inherit">
<SearchIcon />
</IconButton>
<IconButton color="inherit">
<MoreIcon />
</IconButton>
</div>
</Toolbar>
</AppBar>
</React.Fragment>
);
}
}
class TodoList extends React.Component {
render() {
return(
<div className="container">
<form onSubmit={this.props.handleSubmit}>
<div className={"form-group"}>
<label htmlFor={"title"}>Enter A Task Title</label>
<input value={this.props.text} onChange={this.props.handleChange} type="text" className={"form-control"} id="title" rows="3"></input>
</div>
<div className={"form-group"}>
<label htmlFor={"exampleFormControlSelect1"}>Example select</label>
<select onChange={this.props.handlePriority} className={"form-control"} id="exampleFormControlSelect1">
<option value={this.props.priority} onChange={this.props.handlePriority}>Low Priority</option>
<option value={this.props.priorty} >Medium Priority</option>
<option value={this.props.priorty} >High Priority</option>
<option value={this.props.priorty} >Important</option>
<option value={this.props.priorty} >Very Important</option>
</select>
</div>
<button className={"btn btn-primary btn-custom"}>Add : {this.props.items.length + 1}</button>
</form>
</div>
);
}
}
My code contains two classes one of them is setting the stored values in states and my second class is accepting these states through props. My text input works fine, but my select / options selector doesn't seem to save the value selected.
Output:
Thank you for any constructive feedback.
I highly recommend you to split your code into smaller functions.
In particular, I would recommend a class component as a wrapper and function components for your select & option nodes.
First, lets create a function component for the option node
const Option = ({ value, description }) => (
<option value={value}>{description}</option>
);
As you can see, it takes 2 arguments: value & description. Feel free to add your own.
Then, lets create a function component for our select node
const SelectBox = ({ children, onChange, value }) => (
<select onChange={onChange} value={value}>
{children}
</select>
);
And as last step, we put it all together:
class App extends React.Component {
state = {
value: 2
};
handleChange = e => {
this.setState({ value: e.target.value });
};
render() {
return (
<div className="App">
<SelectBox onChange={this.handleChange} value={this.state.value}>
<Option value="1" description="First Item" />
<Option value="2" description="Second Item" />
<Option value="3" description="Third Item" />
</SelectBox>
</div>
);
}
}
Link to codebox

Handling Radio Buttons in Forms with React (Rails 5.1)

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()} />

Categories

Resources