It doesn't work correctly, I want to select only one value, but I can select one and more... and can't deselect
there is code for that function.
const RadioButtonField = ({type, options, disabled, onChange}) => {
const handleChange = (value) => {
onChange(type, value);
};
return (
<div>
{options.map(({ value, label }) =>
<Radio.Group key={value}>
<Radio
disabled={disabled}
onChange={() => handleChange(value)}
>
{label}
</Radio>
</Radio.Group>
)}
</div>
);
};
You can try this for single selection and also you can reselect too
import React from "react";
import "./styles.css";
import Radio from "#material-ui/core/Radio";
export default class App extends React.Component {
state = {
selectedValue: null,
radioOptions: [
{ id: 1, title: "1" },
{ id: 2, title: "2" },
{ id: 3, title: "3" },
{ id: 4, title: "4" }
]
};
handleChange = id => {
this.setState({
selectedValue: id
});
};
render() {
const { selectedValue, radioOptions } = this.state;
return (
<div className="App">
{radioOptions.map(option => {
return (
<div className="radio-parent">
<lable
onClick={() => this.handleChange(option.id)}
className="radio-btn"
>
<Radio
color="default"
value={option.id}
name="radioValue"
checked={selectedValue == option.id}
/>
{option.title}
</lable>
</div>
);
})}
</div>
);
}
}
codesandBox link for Demo
You can refer to the following code:
class App extends React.Component {
state = {
initialValue: "A",
options: ["A", "B", "C", "D"]
};
onChange = e => {
console.log("radio checked", e.target.value);
this.setState({
value: e.target.value
});
};
render() {
return (
<Radio.Group value={this.state.initialValue}>
{this.state.options.map((value, index) => {
return (
<Radio onChange={this.onChange} key={index} value={value}>
{" "}
{value}{" "}
</Radio>
);
})}
</Radio.Group>
);
}
}
Working codesandbox demo
I think you do not need to pass anything to the function. Whenever the option will be clicked, the event (e) object will be passed to onChange(e) and you will get the value of clicked item and checked value in onChange(e) e.target.value and e.target.checked respectively.
Related
I am facing an issue where my 2nd dropdown is receiving values but is not expanding:-
class clusterServersDropdown extends Component {
constructor() {
super();
this.state = {
clusterslist: [],
servertype: [],
selectserver: "",
selectcluster: ""
};
}
componentDidMount() {
this.setState({
clusterslist:[
{label: "cluster1", servertype:["test1","test2","test3"]},
{label: "cluster2", servertype:["test1","test2","test3"]},
{label: "cluster3", servertype:["test1","test2","test3"]},
]
});
}
selectclusterChange(e) {
console.log(e.label)
this.setState({ selectcluster: e.label },() =>{
console.log(this.state.selectcluster)
});
this.setState({
servertype: this.state.clusterslist.find(
(x) => x.label === e.label
).servertype
},() =>{
console.log(this.state.servertype)
}
);
}
routeChange = (e) => {
console.log(e.label)
this.setState({ selectserver: e.label}, () => {
console.log(this.state.selectserver);
let path = "/inventory/cluster/" + this.state.selectcluster +"/servertype/" + this.state.selectserver;
console.log(path);
this.props.history.push(path);
});
};
render() {
return (
<div>
<center>
<label>
Select cluster and server type
<Select className="react-select" classNamePrefix="react-select"
onChange={this.selectclusterChange.bind(this)}
options={this.state.clusterslist.map((x) => {
return {label: x.label};
})}
/>
<Select className="react-select" classNamePrefix="react-select"
onChange={this.routeChange.bind(this)}
options={this.state.servertype.map((y) => {
return {label: y};
})}
/>
</label>
</center>
</div>
);
}
}
export default withRouter(clusterServersDropdown);
Please see below snaps:-
Whenever I am clicking on the 2nd dropdown it retracts and am not able to select the value and proceed. Strangely this works if i replace label by h5, see as below:-
Am i using label wrongly?
Removed label and used h5 with some font formatting instead.
render() {
return (
<div>
<center>
<h5 style={{'font-family':"Pacifico"}}>
Select cluster and server type from dropdown to view its inventory
<Select className="react-select" classNamePrefix="react-select"
onChange={this.selectclusterChange.bind(this)}
options={this.state.clusterslist.map((x) => {
return {label: x.label};
})}
/>
<Select className="react-select" classNamePrefix="react-select"
onChange={this.routeChange}
options={this.state.servertype.map((y) => {
return {label: y};
})}
/>
</h5>
</center>
</div>
);
}
}
Working as expected now.
i am trying to presist the checkbox using local storage...after applying that...
if i select the one checkbox selecting all checkboxes...i using map function for the checkboxes...can any one help on this.....i want to select individual check box
this is the linkmof code sand box :
https://codesandbox.io/s/sparkling-wind-rmz1z?file=/src/App.js
branches.js
import React, { Component } from "react";
import Checkbox from "#material-ui/core/Checkbox";
import FormGroup from "#material-ui/core/FormGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import { List, ListItem } from "#material-ui/core";
import Button from "react-bootstrap/Button";
// const [isChecked, setIsChecked] = useState(true);
class BranchComponent extends Component {
constructor(props) {
super(props);
this.state = {
value: {},
count: 0,
checked: localStorage.getItem("checkbox") === "true" ? true : false
};
}
CheckedBox = e => {
let checked = e.target.checked;
let count = this.state.count;
if (checked) {
console.log(checked);
this.setState({ checked: true });
count++;
} else {
count--;
console.log(checked);
this.setState({ checked: false });
}
this.setState({ count });
console.log(count);
let values = this.state.value;
values[e.target.value] = e.target.checked;
this.setState({ value: values });
localStorage.setItem("checkbox", e.target.checked.toString());
};
checkBoxSubmit = e => {
e.preventDefault();
console.log(this.state.value);
};
render() {
const arr = ["Branch 1", "Branch 2", "Branch 3"];
return (
<React.Fragment>
<form onSubmit={this.checkBoxSubmit}>
<FormGroup aria-label="position" row>
<List className="courses-college-regis">
{arr.map((a, key) => (
<ListItem button key={key}>
<FormControlLabel
label={a}
value={a}
type="checkbox"
name={a}
checked={this.state.checked}
control={<Checkbox color="primary" />}
onClick={this.CheckedBox}
/>
</ListItem>
))}
</List>
</FormGroup>
Count :{this.state.count}
<br />
<Button type="submit" variant="primary">
Submit
</Button>
</form>
</React.Fragment>
);
}
}
export default BranchComponent;
i updated your code is working fine,
import React, { Component } from "react";
import Checkbox from "#material-ui/core/Checkbox";
import FormGroup from "#material-ui/core/FormGroup";
import FormControlLabel from "#material-ui/core/FormControlLabel";
import { List, ListItem } from "#material-ui/core";
import Button from "react-bootstrap/Button";
var localStorageData = localStorage.getItem("checkbox");
// const [isChecked, setIsChecked] = useState(true);
class BranchComponent extends Component {
constructor(props) {
super(props);
this.state = {
value: {},
count:
localStorageData && typeof JSON.parse(localStorageData) === "object"
? JSON.parse(localStorageData).length
: 0,
checked:
localStorageData && typeof JSON.parse(localStorageData) === "object"
? JSON.parse(localStorageData)
: []
};
}
CheckedBox = (e, index) => {
let checked = e.target.checked;
let count = this.state.count;
var addData = [...this.state.checked, index];
if (checked) {
console.log(checked);
this.setState({ checked: [...new Set(addData)] });
count++;
} else {
count--;
console.log(checked);
addData = addData.filter(find => find !== index);
this.setState({ checked: addData });
}
this.setState({ count: addData.length });
console.log(count);
let values = this.state.value;
values[e.target.value] = e.target.checked;
this.setState({ value: values });
localStorage.setItem("checkbox", JSON.stringify([...new Set(addData)]));
};
checkBoxSubmit = e => {
e.preventDefault();
console.log(this.state.value);
};
render() {
const arr = ["Branch 1", "Branch 2", "Branch 3"];
return (
<React.Fragment>
<form onSubmit={this.checkBoxSubmit}>
<FormGroup aria-label="position" row>
<List className="courses-college-regis">
{arr.map((a, key) => (
<ListItem button key={key}>
<FormControlLabel
label={a}
value={a}
type="checkbox"
name={a}
checked={this.state.checked.includes(a)}
control={<Checkbox color="primary" />}
onClick={e => this.CheckedBox(e, a)}
/>
</ListItem>
))}
</List>
</FormGroup>
Count :{this.state.count}
<br />
<Button type="submit" variant="primary">
Submit
</Button>
</form>
</React.Fragment>
);
}
}
export default BranchComponent;
I'm writing a simple to do list app, but the functional component that maps the to-do items in the array is not running again after rendering the initial items and I add another one and I don't' know why.
I can see in the console that the state is being updated with a new item, so I'm just stuck wondering why it's not displaying on the page when I add a new item.
import React from 'react';
class App extends React.Component {
state = { items: [
{
id: 1,
item: 'code another react app'
},
{
id: 2,
item: 'eat some cheetos'
}
],
inputVal: ''
}
onFormSubmit = (e) => {
e.preventDefault();
let newId = Math.random();
let newItem = this.state.inputVal;
this.setState({
items: [
...this.state.items,
{id: newId, item: newItem }
],
inputVal: ''
})
}
toDoList = this.state.items.map(item => {
console.log(item)
return (
<div key={item.id}>
<li>{item.item}</li>
</div>
)
})
render() {
console.log(this.state)
return (
<div>
<form onSubmit={this.onFormSubmit}>
<input
placeholder="add a to do item"
value={this.state.inputVal}
type="text"
onChange={e => this.setState({ inputVal: e.target.value })}
/>
</form>
{this.toDoList}
</div>
);
}
}
export default App;
The toDoList variable is set only one time.
you have to use in render method.
render() {
const toDoList = this.state.items.map(item => {
console.log(item)
return (
<div key={item.id}>
<li>{item.item}</li>
</div>
)
})
return (
<div>
<form onSubmit={this.onFormSubmit}>
<input
placeholder="add a to do item"
value={this.state.inputVal}
type="text"
onChange={e => this.setState({ inputVal: e.target.value })}
/>
</form>
{toDoList}
</div>
);
}
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>
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}
>