I have a input field that I apply a mask to using react-input-mask. I want to change the mask depending on a dropdown value. What is happening is when I change the dropdown value the new mask is applied on the UI but the form value does not get modified. So, when I submit the form the old mask is used. If I modify the value manually in the textbox then the form value change takes affect.
Here is an simplified example:
import React from "react";
import ReactDOM from "react-dom";
import InputMask from "react-input-mask";
import "antd/dist/antd.css";
import { Form, Select } from "antd";
const FormItem = Form.Item;
class FormComponent extends React.Component {
constructor(props) {
super(props);
this.state = { isMaskOne: true };
}
onSelectChange = e => {
if (e === "one") {
this.setState({ isMaskOne: true });
} else {
this.setState({ isMaskOne: false });
}
};
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form>
<FormItem>
<Select onChange={this.onSelectChange}>
<Select.Option value="one" key="one">
one
</Select.Option>
<Select.Option value="two" key="two">
two
</Select.Option>
</Select>
</FormItem>
<FormItem>
{getFieldDecorator("note")(
<InputMask
mask={this.state.isMaskOne ? "999-99-9999" : "99-9999999"}
maskChar=""
/>
)}
</FormItem>
<p>{JSON.stringify(this.props.form.getFieldValue("note"))}</p>
</Form>
);
}
}
const WrappedFormComponent = Form.create()(FormComponent);
const rootElement = document.getElementById("root");
ReactDOM.render(<WrappedFormComponent />, rootElement);
If the numbers 123-45-6789 are enter into the text box with the dropdown selection of "one" then this.props.form.getFieldValue("note") returns 123-45-6789. When I change the dropdown to "two" I would expect this.props.form.getFieldValue("note") to return 12-3456789 but the value remains 123-45-6789 even though the text has changed to the new mask. What am I not understanding?
You need to use ref to access the input masked value, then you need to update the Form.Item using setFieldsValue i.e. this.props.form.setFieldsValue({ note: this.myRef.current.value });
class FormComponent extends React.Component {
constructor(props) {
super(props);
this.state = { isMaskOne: true };
this.myRef = React.createRef();
}
onSelectChange = e => {
if (e === "one") {
this.setState({ isMaskOne: true }, () => {
console.log("ref value:", this.myRef.current.value);
this.props.form.setFieldsValue({ note: this.myRef.current.value });
});
} else {
this.setState({ isMaskOne: false }, () => {
console.log("ref value:", this.myRef.current.value);
this.props.form.setFieldsValue({ note: this.myRef.current.value });
});
}
};
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form>
<FormItem>
<Select onChange={this.onSelectChange}>
<Select.Option value="one" key="one">
one
</Select.Option>
<Select.Option value="two" key="two">
two
</Select.Option>
</Select>
</FormItem>
<FormItem style={{ marginTop: "100px" }}>
{getFieldDecorator("note")(
<InputMask
mask={this.state.isMaskOne ? "999-99-9999" : "99-9999999"}
maskChar=""
ref={this.myRef}
/>
)}
</FormItem>
<p>{JSON.stringify(this.props.form.getFieldValue("note"))}</p>
</Form>
);
}
}
Related
Good day so I have a question about firebase and perhaps my code as well I wrote some code in JSX and React linked to Firebase and the Button that I'm using to delete is not working properly.
I'm using Parent Child props to pass the function into the page that is needed to be deleted but there is no functionality. I need help thanks!
this is the parent where the function is located :
import React from 'react';
import fire from '../config/firebase';
import Modal from 'react-modal';
// import "firebase/database";
// import 'firebase/auth';
import NotesCard from './note-card';
Modal.setAppElement('#root');
export default class Notes extends React.Component {
_isMounted = false;
constructor(props) {
super(props);
this.state = {
notes: [],
showModal: false,
loggedin: false
};
this.handleOpenModal = this.handleOpenModal.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
this.handleAddNote = this.handleAddNote.bind(this);
this.handleRemoveNote = this.handleRemoveNote.bind(this);
}
componentDidMount() {
this._isMounted = true;
fire.auth().onAuthStateChanged((user) => {
if(user){
// call firebase from import fire
// grab userData and push it to the dataArray
fire.database().ref(`users/${user.uid}/notes`).on('value', (res) => {
const userData = res.val()
const dataArray = []
for(let objKey in userData) {
userData[objKey].key = objKey
dataArray.push(userData[objKey])
}
// set in the state
if(this._isMounted){
this.setState({
notes: dataArray,
loggedin: true
})
}
});
}else {
this.setState({loggedin: false})
}
});
};
componentWillUnmount() {
this._isMounted = false;
}
handleAddNote (e) {
e.preventDefault()
const note = {
title: this.noteTitle.value,
text: this.noteText.value
}
// reference where we can push it
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes`);
dbRef.push(note)
this.noteTitle.value = ''
this.noteText.value = ''
this.handleCloseModal()
}
handleRemoveNote(key) {
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes/${key}`);
dbRef.remove();
}
handleOpenModal (e) {
e.preventDefault();
this.setState({
showModal: true
});
}
handleCloseModal () {
this.setState({
showModal: false
});
}
render() {
return (
<div>
<button onClick={this.handleOpenModal}>create Note</button>
<section className='notes'>
{
this.state.notes.map((note, indx) => {
return (
<NotesCard
note={note}
key={`note-${indx}`}
handleRemoveNote={this.handleRemoveNote}
/>
)
}).reverse()
}
</section>
<Modal
isOpen={this.state.showModal}
onRequestClose={this.handleCloseModal}
shouldCloseOnOverlayClick={false}
style={
{
overlay: {
backgroundColor: '#9494b8'
},
content: {
color: '#669999'
}
}
}
>
<form onSubmit={this.handleAddNote}>
<h3>Add New Note</h3>
<label htmlFor='note-title'>Title:</label>
<input type='text' name='note-title' ref={ref => this.noteTitle = ref} />
<label htmlFor='note-text'>Note</label>
<textarea name='note-text' ref={ref => this.noteText = ref} placeholder='type notes here...' />
<input type='submit' onClick={this.handleAddNote} />
<button onClick={this.handleCloseModal}>close</button>
</form>
</Modal>
</div>
)
}
}
and this is where the function is being called :
import React from 'react';
import fire from '../config/firebase';
export default class NotesCard extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: false,
note: {}
}
this.handleEditNote = this.handleEditNote.bind(this);
this.handleSaveNote = this.handleSaveNote.bind(this);
}
handleEditNote() {
this.setState({
editing: true
})
}
handleSaveNote(e) {
e.preventDefault()
const userId = fire.auth().currentUser.uid;
const dbRef = fire.database().ref(`users/${userId}/notes/${this.props.note.key}`);
dbRef.update({
title: this.noteTitle.value,
text: this.noteText.value
})
this.setState({
editing: false
})
}
render() {
let editingTemp = (
<span>
<h4>{this.props.note.title}</h4>
<p>{this.props.note.text}</p>
</span>
)
if(this.state.editing) {
editingTemp = (
<form onSubmit={this.handleSaveNote}>
<div>
<input
type='text'
defaultValue={this.props.note.title}
name='title'
ref={ref => this.noteTitle = ref}
/>
</div>
<div>
<input
type='text'
defaultValue={this.props.note.text}
name='text'
ref ={ref => this.noteText = ref}
/>
</div>
<input type='submit' value='done editing' />
</form>
)
}
return (
<div>
<button onClick={this.handleEditNote}>edit</button>
<button onClick={this.props.handleRemoveNote(this.state.note.key)}>delete</button>
{editingTemp}
</div>
)
}
}
Thank you in advance for taking a look at this code.
Second iteration answer
Working sandbox
Problem
looking at https://codesandbox.io/s/trusting-knuth-2og8e?file=/src/components/note-card.js:1621-1708
I see that you have this line
<button onClick={()=> this.props.handleRemoveNote(this.state.note.key)}>delete
Yet your state.note declared as an empty map in the constructor:
this.state = {
editing: false,
note: {}
}
But never assigned a value using this.setState in the component
Solution
Change it to:
<button onClick={()=> this.props.handleRemoveNote(**this.props.note.key**)}>delete</button>
First iteration answer
NotesCard's buttons is firing the onClick callback on render instead on click event.
This is because you have executed the function instead of passing a callback to the onClick handler
Change
<button onClick={this.props.handleRemoveNote(this.state.note.key)}>delete</button>
To
<button onClick={()=> this.props.handleRemoveNote(this.state.note.key)}>delete</button>
The project uses React Bootstrap https://react-bootstrap.github.io/
The app receives a list of buckets from api. There are two select dropdowns. The objective here is that the same bucket can not be selected in both selects. So if the first select has "Red" bucket selected, the second dropdown can not have "Red" in its list.
I have achieved that the second list gets created and the states are updated correctly. But when the second dropdown rerenders, in certain situation, it shows the wrong entry as selected.
The steps:
initial load:
The 1st list [Red, Green, Blue]. Selected: Red.
The 2nd list [Green, Blue]. Selected: Green
in the first dropdown select Green:
The 2nd list [Red, Blue]. But the selected option is Blue. It should be Red, as it is the first available from the list.
I understand that it may be because Red was not previously in the list of the 2nd dropdown. But shouldn't that matter? Seeing that the list has been updated and the component has rerendered.
import React, { Component } from "react";
import Form from "react-bootstrap/Form";
import ToBucket from "./toBucket";
class Buckets extends Component {
constructor(props) {
super(props);
this.state = {
fromBucketList: [],
toBucketList: [],
selectedFromBucket: [],
selectedToBucket: [],
};
}
handleFromBucketChange = (event) => {
let nam = event.target.name;
let bucketId = event.target.value;
const selectedFromBucket = this.getBucketById(bucketId);
let rebuiltToBucketList = [...this.state.fromBucketList];
let selectedFromBucketIndex = rebuiltToBucketList.indexOf(
selectedFromBucket
);
rebuiltToBucketList.splice(selectedFromBucketIndex, 1);
this.setState({ toBucketList: rebuiltToBucketList });
this.setState({ [nam]: selectedFromBucket });
this.setState({ selectedToBucket: rebuiltToBucketList[0] });
};
handleToBucketChange = (event) => {
let nam = event.target.name;
let bucketId = event.target.value;
const selectedToBucket = this.getBucketById(bucketId);
this.setState({ [nam]: selectedToBucket });
};
componentDidMount() {
const buckets = [
{ id: 1, color: "red" },
{ id: 2, color: "green" },
{ id: 3, color: "blue" },
];
let initialToBucketList = [...buckets];
initialToBucketList.splice(0, 1);
this.setState({ fromBucketList: buckets });
this.setState({ toBucketList: initialToBucketList });
this.setState({ selectedFromBucket: buckets[0] });
this.setState({ selectedToBucket: initialToBucketList[0] });
}
getBucketById = (id) => {
let foundedBucket = this.state.fromBucketList.find(
(element) => element.id == id
);
return foundedBucket;
};
render() {
return (
<React.Fragment>
<Form noValidate validated={this.state.validated}>
<Form.Group controlId="SelectFromBucket">
<Form.Label>Select FROM bucket:</Form.Label>
<Form.Control
required
type="text"
as="select"
onChange={this.handleFromBucketChange}
name="selectedFromBucket"
>
{this.state.fromBucketList.map((bucket) => (
<option key={bucket.id} value={bucket.id}>
{bucket.color}
</option>
))}
</Form.Control>
</Form.Group>
<React.Fragment>
<ToBucket
handleToBucketChange={this.handleToBucketChange}
toBucketList={this.state.toBucketList}
/>
</React.Fragment>
<p>
From :{this.state.selectedFromBucket.color}
<br></br>
To :{this.state.selectedToBucket.color}
</p>
</Form>
</React.Fragment>
);
}
}
export default Buckets;
The 2nd dropdown:
import React, { Component } from "react";
import Form from "react-bootstrap/Form";
class ToBucket extends Component {
render() {
return (
<React.Fragment>
<Form.Group controlId="SelectToBucket">
<Form.Label>Select TO bucket:</Form.Label>
<Form.Control
required
type="text"
as="select"
onChange={this.props.handleToBucketChange}
name="selectedToBucket"
>
{this.props.toBucketList.map((bucket) => (
<option key={bucket.id} value={bucket.id}>
{bucket.color}
</option>
))}
</Form.Control>
</Form.Group>
</React.Fragment>
);
}
}
export default ToBucket;
I would prefer an answer that is not a workaround or hack for this code, but a conceptual guide to another directions if this aproach is not correct.
The FormControl of the second dropdown might not change its value because there is no value props passing to it.
Try passing the selected value from the first bucket to the second bucket as props
I never use react-bootstrap but this snippet should works.
First Component
<ToBucket
value={this.state.selectedToBucket && this.state.selectedToBucket.id}
handleToBucketChange={this.handleToBucketChange}
toBucketList={this.state.toBucketList}
/>
Second Component
import React, { Component } from "react";
import Form from "react-bootstrap/Form";
class ToBucket extends Component {
render() {
return (
<React.Fragment>
<Form.Group controlId="SelectToBucket">
<Form.Label>Select TO bucket:</Form.Label>
<Form.Control
required
value={this.props.value}
type="text"
as="select"
onChange={this.props.handleToBucketChange}
name="selectedToBucket"
>
{this.props.toBucketList.map((bucket) => (
<option key={bucket.id} value={bucket.id}>
{bucket.color}
</option>
))}
</Form.Control>
</Form.Group>
</React.Fragment>
);
}
}
export default ToBucket;
I made a PoC to see how to handle my change detection on a dynamic list of checkboxes (note, i do not know beforehand how many checkboxes i have.) I created a n ES6 map (Dictionary) that tracks the checked state of each individual checkbox. but for some reason I get the following error:
A component is changing an uncontrolled input of type checkbox 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
Usually when my number of form input fields is known i track them via the state, but how would one handle this case. The logic works fine, but I need to get rid of the error.
My app code:
import React, { Component } from "react";
import Checkbox from "./checkbox";
class App extends Component {
constructor(props) {
super(props);
this.state = {
checkedItems: new Map()
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = e => {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({
checkedItems: prevState.checkedItems.set(item, isChecked)
}));
};
deleteCheckboxState = (name, checked) => {
const updateChecked = typeof checked === "undefined" ? true : false;
this.setState(prevState => prevState.checkedItems.set(name, updateChecked));
};
clearAllCheckboxes = () => {
const clearCheckedItems = new Map();
this.setState({ checkedItems: clearCheckedItems });
};
render() {
const checkboxes = [
{
name: "check-box-1",
key: "checkBox1",
label: "Check Box 1"
},
{
name: "check-box-2",
key: "checkBox2",
label: "Check Box 2"
},
{
name: "check-box-3",
key: "checkBox3",
label: "Check Box 3"
},
{
name: "check-box-4",
key: "checkBox4",
label: "Check Box 4"
}
];
const checkboxesToRender = checkboxes.map(item => {
return (
<label key={item.key}>
{item.name}
<Checkbox
name={item.name}
checked={this.state.checkedItems.get(item.name)}
onChange={this.handleChange}
type="checkbox"
/>
</label>
);
});
const checkboxesDeleteHandlers = checkboxes.map(item => {
return (
<span
key={item.name}
onClick={() =>
this.deleteCheckboxState(
item.name,
this.state.checkedItems.get(item.name)
)
}
>
{item.name}
</span>
);
});
return (
<div className="App">
{checkboxesToRender}
<br /> {checkboxesDeleteHandlers}
<p onClick={this.clearAllCheckboxes}>clear all</p>
</div>
);
}
}
export default App;
The checkbox reusable component:
import React from "react";
class Checkbox extends React.Component {
render() {
return (
<input
type={this.props.type}
name={this.props.name}
checked={this.props.checked}
onChange={this.props.onChange}
/>
);
}
}
export default Checkbox;
The problem is that the checked state is initially undefined, which is a falsy value, but interpreted as not provided.
So you can simply ensure that the falsy state will actually be false by using !!.
So change the line
checked={this.state.checkedItems.get(item.name)}
to this
checked={!!this.state.checkedItems.get(item.name)}
React gives you this warning because it likes that you chose between controlled and uncontrolled components.
In the case of a checkbox input the component is considered controlled when its checked prop is not undefined.
I've just given a default value for checked and changed the code testing for undefined a little bit.
The warning should be gone.
// import React, { Component } from "react";
class Checkbox extends React.Component {
static defaultProps = {
checked: false
}
render() {
return (
<input
type={this.props.type}
name={this.props.name}
checked={this.props.checked}
onChange={this.props.onChange}
/>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checkedItems: new Map()
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = e => {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({
checkedItems: prevState.checkedItems.set(item, isChecked)
}));
};
deleteCheckboxState = (name, checked) => {
const updateChecked = checked == null ? true : false;
this.setState(prevState => prevState.checkedItems.set(name, updateChecked));
};
clearAllCheckboxes = () => {
const clearCheckedItems = new Map();
this.setState({ checkedItems: clearCheckedItems });
};
render() {
const checkboxes = [
{
name: "check-box-1",
key: "checkBox1",
label: "Check Box 1"
},
{
name: "check-box-2",
key: "checkBox2",
label: "Check Box 2"
},
{
name: "check-box-3",
key: "checkBox3",
label: "Check Box 3"
},
{
name: "check-box-4",
key: "checkBox4",
label: "Check Box 4"
}
];
const checkboxesToRender = checkboxes.map(item => {
return (
<label key={item.key}>
{item.name}
<Checkbox
name={item.name}
checked={this.state.checkedItems.get(item.name) || false}
onChange={this.handleChange}
type="checkbox"
/>
</label>
);
});
const checkboxesDeleteHandlers = checkboxes.map(item => {
return (
<span
key={item.name}
onClick={() =>
this.deleteCheckboxState(
item.name,
this.state.checkedItems.get(item.name)
)
}
>
{item.name}
</span>
);
});
return (
<div className="App">
{checkboxesToRender}
<br /> {checkboxesDeleteHandlers}
<p onClick={this.clearAllCheckboxes}>clear all</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
What I want to do is to be able to toggle an active class on my elements that are dynamically created, as to be able to change the css for the selected checkbox, giving the impression that a certain filter is selected. I have looked at so many solutions and guides to make this work for my app, but I can't seem to implement it correctly. Any help would be appreciated.
Checkboxes component
import React from 'react';
const Checkbox = (props) => {
const { label, subKey } = props;
const sub1 = `${subKey}1`;
return (
<label htmlFor={sub1} className="check_label">
{label}
<input
type="checkbox"
id={sub1}
checked={props.isChecked}
onChange={props.handleCheck}
onClick={() => console.log(label)}
value={`${label.toLowerCase()}/?search=`}
/>
</label>
);
};
export default Checkbox;
and the Search component that implements checkboxes
import React, { Component } from 'react';
import Checkbox from './Checkbox';
const APIQuery = 'https://swapi.co/api/';
const searchLabels = ['Planets', 'Starships', 'People', 'Species', 'Films', 'Vehicles'];
export default class Searchbutton extends Component {
constructor(props) {
super(props);
this.state = {
endpointValue: '',
searchValue: '',
};
}
/* Funcionality to handle form and state of form */
/* Changes state of value whenever the form is changed, in realtime. */
handleChange(event) {
this.setState({ searchValue: event.target.value });
}
/* Prevents default formsubmit */
handleSubmit(event) {
event.preventDefault();
}
/* Handles state of checkboxes and sets state as to prepend necessary filter for request */
handleCheck(event) {
this.setState({ endpointValue: event.target.value });
if (this.state.endpointValue === event.target.value) {
this.setState({ endpointValue: '' });
}
}
/* Creates the checkboxes dynamically from the list of labels. */
createBoxes() {
const checkboxArray = [];
searchLabels.map(item => checkboxArray.push(
<Checkbox
key={item}
className="madeBoxes"
subKey={item}
endpointValue={this.state.endpointValue}
handleChange={e => this.handleChange(e)}
handleCheck={e => this.handleCheck(e)}
label={item}
/>,
));
return checkboxArray;
}
render() {
return (
<div className="search_content">
<div className="search_wrapper">
<form onSubmit={this.handleSubmit} method="#">
<label htmlFor="searchBar">
<input type="text" id="searchbar" className="search_bar" value={this.state.searchValue} onChange={e => this.handleChange(e)} />
</label>
<div>
<input type="submit" className="search_button" value="May the Force be with you." onClick={() => this.props.searchWithApi(APIQuery + this.state.endpointValue + this.state.searchValue)} />
</div>
</form>
</div>
<div className="checkboxes">
{this.createBoxes(this.labels)}
</div>
<div className="sort_filters">
{' '}
{/* These are options that the user can make in order to sort and filter the results.
The idea is to make it so that changing the value auto-perform a new request */}
{/* For sorting the returned objects based on user choice */}
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid, until href added */}
Choose sort method
<ul className="sorting">
<li className="sort_optn" href="#" value="lexicographical">Alphabetically</li>
<li className="sort_optn" href="#" value="by_added_date">By added date</li>
<li className="sort_optn" href="#" value="by_added_date_rev">By added date reversed</li>
</ul>
</div>
</div>
);
}
}
You don't really have to do it with react. You can reformat your code a little bit and solve it with CSS :checked pseudo-class.
In particular, don't wrap your checkbox within a label, but instead put the label after the input. Check this fiddle for example: https://jsfiddle.net/8c7a0fx5/
You can use the styled-component package. check the example below on how to use it:
import { Component } from 'react'
import { render } from 'react-dom'
import styled from 'styled-components'
const StyledCheckbox = styled.div`
label {
background: ${props => props.active ? 'red': 'white'}
}
`
class MyAwesomeComponent extends Component {
constructor(){
super()
this.state = {
isChecked: false
}
this.handleOnChange = this.handleOnChange.bind(this)
}
handleOnChange = ()=>{
this.setState({
isChecked: !this.state.isChecked,
})
}
render(){
const { isChecked } = this.state
return(
<StyledCheckbox active={isChecked}>
<label>Names</label>
<input type="checkbox" onChange={this.handleOnChange} />
</StyledCheckbox>
)
}
}
render(<MyAwesomeComponent/>, document.getElementById('root'))
Working code on codepen.io
I have a form that dynamically generates inputs, where one input is a material-ui TextField and SelectField with multiple options. I am having a problem with telling the select fields apart from each other though. In an ideal world I would like to be able to collect the data from both of these inputs and store them as an object (i.e. {name: Employee, type_id: 1}), which will become an array of objects depending on how many inputs are generated.
My current code looks like this:
import React from 'react';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import DatatypeStore from '../../stores/DatatypeStore';
const styles = {
customWidth: {
width: 100,
},
};
class MultipleEntry extends React.Component {
state={inputs: [], columnHeaders: [], value: 1};
addInputField(e) {
e.preventDefault();
let inputs = this.state.inputs;
inputs.push({name: null});
this.setState({inputs});
}
handleChange(e, index, value) {
const isSelectField = value !== undefined;
if (isSelectField) {
console.log(index, value);
} else {
console.log(e.target.value);
}
}
render() {
let {inputs, columnHeaders, value} = this.state;
return (
<div className="col-md-12">
{inputs.map((input, index) => {
let name = "header " + index;
return (
<div key={index}>
<br />
<TextField
hintText="Enter the name for the column"
floatingLabelText="Column Name"
type="text"
name={name}
onChange={e => this.handleChange(e)}
/>
<SelectField
value={this.state.value}
onChange={e => this.handleChange(e, index, value)}
style={styles.customWidth}
>
{DatatypeStore.getDatatypes().map(el => {
return <MenuItem key={el.id} value={el.id} primaryText={el.name} />;
})}
</SelectField>
<br />
</div>
);
})}
<br/>
<RaisedButton
style={{marginTop: 50}}
label="Add column input"
secondary={true}
onClick={e => this.addInputField(e)}
/>
<br />
</div>
);
}
}
export default MultipleEntry;
So yeah, examples doing what I would like to do would be much appreciated. If you can do it using material-ui components so much the better!
Thanks for your time
Update
Here is the parent component
import React from 'react';
import MultipleEntry from './MultipleEntry.jsx';
import Paper from 'material-ui/Paper';
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
import TokenStore from '../../stores/TokenStore';
const styles = {
paper: {
marginTop: 50,
paddingBottom: 50,
width: '100%',
textAlign: 'center',
display: 'inline-block',
},
};
class ColumnHeaderForm extends React.Component {
state = {title: '', input: null};
changeValue(e) {
const title = e.target.value;
this.setState({
title
});
}
handleInputChange(columnHeaderArray) {
let input = this.state.input;
input = columnHeaderArray;
this.setState({input});
}
handleFormSubmit(e) {
e.preventDefault();
let access_token = TokenStore.getToken();
let title = this.state.title;
let inputs = this.state.input;
this.props.handleFormSubmit(access_token, title, inputs);
}
render() {
let {title, input} = this.state;
return (
<div>
<Paper style={styles.paper}>
<form role="form" autoComplete="off">
<div className="text-center">
<h2 style={{padding: 10}}>Fill out the column names (you can add as many as you need)</h2>
<div className="col-md-12">
<TextField
hintText="Enter a title for the table"
floatingLabelText="Title"
type="text"
onChange={e => this.changeValue(e)}
/>
</div>
<div className="col-md-12">
<MultipleEntry handleInputChange={this.handleInputChange.bind(this)} />
</div>
<RaisedButton
style={{marginTop: 50}}
label="Submit"
primary={true}
onClick={e => this.handleFormSubmit(e)}
/>
</div>
</form>
</Paper>
</div>
);
}
}
export default ColumnHeaderForm;
well from my understanding you want to handle the TextField and SelectField onChange in the same method. They do have different signatures
TextField (event, value)
SelectField (event, index, value)
But you can achieve it easily by testing the third argument for example:
handleChange(event, index, value) {
const isSelectField = value !== undefined;
if(isSelectField) {
// do whatever you need to do with the SelectField value
} else {
// do whatever you need to do with the TextField value
}
}
Note:
You shouldn't mutate your state, that's wrong.
let columnHeaders = this.state.columnHeaders;
columnHeaders[e.target.name] = e.target.value;
To avoid it you can "clone" the state object and apply the changes there..
Object.assign({}, this.state.columnHeaders, {
[e.target.name]: event.target.value
})
Read more about Object.assign here: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
----------------------------------------------------
UPDATED EXAMPLE 26/04/2016
Now you can see I'm just changing the typeId inside the input object (that I found by its id) for SelectFields. And almost the same thing for TextField - just change the field name..
handleChange(inputId, event, index, value) {
const isSelectField = value !== undefined;
if(isSelectField) {
this.setState({
inputs: this.state.inputs.map((input) => {
return input.id === inputId ? Object.assign({}, input, {
typeId: value
}) : input
})
})
} else {
this.setState({
inputs: this.state.inputs.map((input) => {
return input.id === inputId ? Object.assign({}, input, {
name: event.target.value
}) : input
})
})
}
}
//Make sure the id is unique for each input
addInputField(e) {
e.preventDefault();
this.setState({
inputs: this.state.inputs.concat({ id: 1, name: null })
});
}
//Binding the ID in the call so we can use it in that method..
<SelectField
value={input.typeId}
onChange={this.handleChange.bind(this, input.id)}
style={styles.customWidth}
>