When i choose check in checkbox the value should be inserted in hobby array and when i unchecked the value should be removed from array
value is inserting and removing from array but i am not able to check and unchecked the checkbox visually. but in state it's working.
const ContactForm = () => {
const [state,setContact] = useState({
userId:"",
firstName:"",
lastName:"",
jobTitleName:"",
employeeCode:"",
emailAddress:"",
phoneNumber:"",
country:"",
status:"active",
hobbies:[]
})
const onChange = (event)=>{
setContact({...state,[event.target.name]: event.target.value});
}
e.preventDefault();
}
return (
<div className="form-group mb-2">
<label className="mb-0 font-weight-bold">Hobbies</label>
<div className="form-check">
<input type="checkbox" value="Basketball" onChange={(e)=>{
if(e.target.checked)
{
state.hobbies.push(e.target.value)
console.log(state.hobbies)
}else {
var index = state.hobbies.indexOf(e.target.value);
if (index > -1) {
state.hobbies.splice(index, 1);
console.log(state.hobbies)
}
}
}
}
/>
<label className="form-check-label">
Basketball
</label>
</div>
<div className="form-check">
<input type="checkbox" value="Vollyball" onChange={(e)=>{
if(e.target.checked)
{
state.hobbies.push(e.target.value)
console.log(state.hobbies)
}else {
var index = state.hobbies.indexOf(e.target.value);
if (index > -1) {
state.hobbies.splice(index, 1);
console.log(state.hobbies)
}
}
}
}
/>
<label className="form-check-label">
Vollyball
</label>
</div>
</div>
)
For your reference sample application
how to use multiple checkbox and adding multiple values.
const checkboxes = [
'Cricket', 'Footbal', 'Tennis'
];
class App extends Component {
state = {
checkedItems: new Map(),
}
handleChange=(e)=> {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({ checkedItems: prevState.checkedItems.set(item, isChecked) }));
}
render() {
console.log(this.state.checkedItems)
return (
<>
{
checkboxes.map(item => (
<label key={item}>
{item}
<input type="checkbox"
checked={this.state.checkedItems.get(item)}
onChange={this.handleChange}
name={item}/>
</label>
))
}
</>
);
}
}
You are mutating state. Your updating state value witout useState's method setContact.
Please find working code here: https://codesandbox.io/s/great-diffie-giqgr
When you mutate state or change state value without useState method or setState it won't re-render component even though your state value is changed. You onChange should be like this.
if (e.target.checked) {
const newState = {
...state,
hobbies: [...state.hobbies, e.target.value]
};
setContact(newState);
console.log("** checked", newState);
// state.hobbies.push(e.target.value);
// console.log(state.hobbies);
} else {
const newState = {
...state,
hobbies: state.hobbies.filter(prev => prev !== e.target.value)
};
console.log("** unchecked", newState);
setContact(newState);
}
Related
Set the attributes of a input field or any component by taking input from the user dynamically?
I would like to know if there is any way, where I would give user an option to choose a component from the list of components i would mention, and allow him to customize the components attributes. For example if the user chooses a Input component, he must be able to set the attributes of that particular component, like "required", "type", "placeholder".
You can achieve it by passing all attributes you want as props to the child component.
You should also add them to state of parent component with change handler.
Each time the user change something of the attributes, the state should update.
As the state updates, the new state will pass as props to child Component and it'll update.
I made a simple example to input: You can change its placeholder, minLength, and requierd.
Check This Example
in the render, method you can do something like this
render() {
// change the name and values base on your user input
userInputtedAttribName = "placeholder";
userInputtedAttribValue = "the placeholder";
// the object to contain your user defined attribute name and values
const dynamicAttributes = {
[userInputtedAttribName]: userInputtedAttribValue
};
return (
<div>
<input type="text" {...dynamicAttributes}></input>
</div>
)
}
the spread operator, {...dynamicAttributes}, will build the attributes and their values dynamically
Probably not even what you're looking for, but I made a medium-sized prototype that can show you how to create Components (input, button, textarea), dynamically.
It's like filling out a form. Choose a type of component you want to make from the select-list. Then define the attributes you want in the proceeding textboxes. Once you're done adding all the attributes, hit Generate to render your customized component.
Sandbox: https://codesandbox.io/s/dynamic-component-generator-mhuh5
Working code:
import React from "react";
import ReactDOM from "react-dom";
import Input from "./Input";
import Button from "./Button";
import TextArea from "./TextArea";
import "./styles.css";
class ComponentGenerator extends React.Component {
state = {
componentInProgress: null,
customizeMode: false,
attributeName: "",
attributeSetting: "",
boolean: false,
builtComponents: []
};
handleSelection = e => {
this.setState({
componentInProgress: { componentType: e.target.value },
customizeMode: true
});
};
createOptions = () => {
const { customizeMode, componentInProgress } = this.state;
return (
<div>
<h4>Choose a Component:</h4>
<select
onChange={this.handleSelection}
value={!customizeMode ? "Select" : componentInProgress.componentType}
>
<option>Select</option>
<option>Input</option>
<option>TextArea</option>
<option>Button</option>
</select>
</div>
);
};
handleOnChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
handleOnSubmit = e => {
const {
attributeName,
attributeSetting,
boolean,
componentInProgress
} = this.state;
e.preventDefault();
let componentCopy = JSON.parse(JSON.stringify(componentInProgress));
componentCopy.props = {
...componentCopy.props,
[attributeName]: boolean ? boolean : attributeSetting
};
this.setState({
componentInProgress: componentCopy,
attributeName: "",
attributeSetting: "",
boolean: false
});
};
setBoolean = boolean => {
this.setState({
boolean: boolean
});
};
generateComponent = () => {
const { componentInProgress, builtComponents } = this.state;
this.setState({
componentInProgress: null,
customizeMode: false,
builtComponents: [...builtComponents, componentInProgress]
});
};
defineComponentAttributes = () => {
const {
componentInProgress,
attributeName,
attributeSetting,
boolean
} = this.state;
return (
<div>
<h4>
Customizing:{" "}
<span className="highlight">{componentInProgress.componentType}</span>
</h4>
{/*Render form */}
<form onSubmit={this.handleOnSubmit}>
<label>Attribute: </label>
<input
className="form-group"
onChange={this.handleOnChange}
value={attributeName}
name="attributeName"
placeholder="Choose attribute (type)"
/>
<label>Definition: </label>
<input
className="form-group"
onChange={this.handleOnChange}
value={attributeSetting}
name="attributeSetting"
placeholder="Define attribute (text)"
/>
<label>This is a Boolean type: </label>
<input
type="radio"
name="boolean"
onChange={() => this.setBoolean(true)}
/>
True
<input
type="radio"
name="boolean"
checked={boolean === false}
onChange={() => this.setBoolean(false)}
/>
False
<button className="form-group" type="submit">
Add
</button>
</form>
{/*Create List of attributes */}
{componentInProgress.props && (
<div>
<h4>Defined Attributes:</h4>
{Object.entries(componentInProgress.props).map(
([propName, propValue]) => {
return (
<div key={propName}>
<span>{propName}: </span>
<span>{propValue + ""}</span>
</div>
);
}
)}
</div>
)}
<div>
<h4>Click to finish and generate:</h4>
<button onClick={this.generateComponent}>Generate</button>
</div>
</div>
);
};
renderComponents = () => {
const { builtComponents } = this.state;
return builtComponents.map((component, index) => {
let renderedComponent = () => {
switch (component.componentType) {
case "Input":
return <Input {...component.props} />;
case "Button":
return <Button {...component.props} />;
case "TextArea":
return <TextArea {...component.props} />;
default:
return null;
}
};
return (
<div key={index} className="componentSection">
<h4>{component.componentType}</h4>
{renderedComponent()}
<div>
<p>Attributes: </p>
{Object.entries(component.props).map(([propName, propValue]) => {
return (
<div key={propName}>
<span>{propName}: </span>
<span>{propValue + ""}</span>
</div>
);
})}
</div>
</div>
);
});
};
render() {
const { customizeMode } = this.state;
return (
<div>
{this.createOptions()}
{customizeMode && this.defineComponentAttributes()}
<div className="components">{this.renderComponents()}</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ComponentGenerator />, rootElement);
Hi guys i have this problem. I have input and dropdown I need choose from option dropdown and write value on input but first value was everytime empty. I solved this problem with componentDidUpdate and set locationValue on undefined. After i have sometimes this error.How can I fixed it? I need change lifecycle method or what ?
Here is code
class AccordionForm extends Component {
state = {
value: '',
locationValue: undefined,
};
componentDidUpdate() {
const { nameOptions } = this.props;
if (nameOptions && nameOptions && this.state.locationValue === undefined) {
this.setState({
locationValue: nameOptions[0],
});
}
}
handleChangeInputSelect = ({ target }) => {
this.setState({
locationValue: target.value,
});
};
handleChangeInput = ({ target }) =>
this.setState({
value: target.value,
});
onSubmit = event => {
const { value, locationValue } = this.state;
const { handleSubmitForm } = this.props;
event.preventDefault();
handleSubmitForm(value, locationValue);
this.setState({ value: '' });
};
render() {
const { name, nameOptions } = this.props;
const { value } = this.state;
return (
<Form className="accordion_form" name={name} onSubmit={this.onSubmit}>
<FormGroup className="form-group-locations">
{nameOptions && (
<Input
className="form-input"
required
name={name}
type="select"
onChange={this.handleChangeInputSelect}
>
{nameOptions.map((option, index) => {
return (
<option key={index} value={option}>
{option}
</option>
);
})}
</Input>
)}
<Input
placeholder={`add new ${name}...`}
type="text"
required
name={name}
value={value}
onChange={this.handleChangeInput}
className="form-input"
/>
<Button className="tagBtn" color="success" type="submit">
Add
</Button>
</FormGroup>
</Form>
);
}
}
export default AccordionForm;
This could be happening because at the first instance your nameOptions[0] is undefined. Please update your if statement's conditions like this:
componentDidUpdate() {
const { nameOptions } = this.props;
if (nameOptions && !!nameOptions.length && nameOptions[0] && this.state.locationValue !== nameOptions[0]) {
this.setState({
locationValue: nameOptions[0],
});
}
}
I'm creating an input element using document.createElement and setting the attribute. i would like to have an onchange event which will update the state.
how can i proceed? following is my code
addoffers = () => {
var input = document.createElement("input");
input.setAttribute('name', 'whatweoffer'.concat(Math.floor((Math.random() * 1000) + 1)));
input.setAttribute('class','form-control mb-3');
input.setAttribute('placeholder','what we offer');
input.onchange = this.handleChange;
var parent = document.getElementById("offerinput");
parent.appendChild(input);
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(e.target.value);
}
<input type="text" className="form-control mb-3" id='whatweoffer' name='whatweoffer' onChange={this.handleChange} placeholder='what we offer' required />
Try this
class DynamicInputs {
constructor(props) {
super(props);
this.state = {
inputs : []
}
}
render() {
return (
<React.Fragment>
{
this.state.inputs.map(inputValue =>{
<input key= {inputValue} type="text" placeholder="what we offer" className="form-control mb-3" name = {`whatweoffer${inputValue}`} onChange = {this.handleChange} required/> })
}
</React.Fragment>
)
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(e.target.value);
}
addInput() = () =>{
let {inputs} = this.state;
inputs.push((Math.floor((Math.random() * 1000) + 1))
this.setState({
inputs
})
}
}
Although there's accepted answer but I want to give you another approach.
Instead of create input dynamically, why don't you just use own component's state to decide if an input is displayed or not, and other attributes are just state values. For example:
render() {
const {
isInputDisplayed, inputName, inputId, inputClasses, inputPlaceholder,
} = this.state;
return (
{ isInputDisplayed && (
<input type="text" name={inputName} id={inputId} className={inputClasses} placeholder={inputPlaceholder} />
)}
);
}
Given a react form with multiple radio buttons and a text input that is visible only when the other option radio button is selected, I currently have the submit button disabled until a radio button is selected. However, if the other option is selected, the submit button will still work even if there is no text in the input field associated with it. How can I check the length of the input box if and only if the other option is selected?
class CancelSurvey extends React.Component {
constructor (props) {
super(props)
this.state = {
reasons: [],
reason: {},
otherReasonText: undefined,
submitting: false
}
this.processData = this.processData.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.setReason = this.setReason.bind(this)
this.setOtherReasonText = this.setOtherReasonText.bind(this)
this.otherReason = {
reason_id: 70,
client_reason: 'other'
}
}
componentDidMount () {
this.fetchSurvey()
}
/**
* Fetch reasons
*/
fetchSurvey (cb) {
superagent
.get('/api/user/survey')
.then(this.processData)
}
processData (data) {
this.setState({ reasons: data.body })
}
async handleSubmit (e) {
e.preventDefault()
await this.setState({ submitting: true })
const { reason, otherReasonText } = this.state
superagent
.post('/api/user/survey')
.send({
optionId: reason.reason_id,
optionText: reason.client_reason,
otherReasonText
})
.then(async (data) => {
await this.setState({ submitting: false })
if (data.body.success) {
this.props.setStep(OFFER)
}
})
}
setOtherReasonText (e) {
this.setState({ otherReasonText: e.target.value })
}
setReason (reason) {
this.setState({ reason })
}
/**
* render
*/
render (props) {
const content = this.props.config.contentStrings
const reasons = this.state.reasons.map((reason, i) => {
return (
<div
className='form-row'
key={i}>
<input type='radio'
id={reason.reason_id}
value={reason.client_reason}
name='reason'
checked={this.state.reason.reason_id === reason.reason_id}
onChange={() => this.setReason(reason)} />
<label htmlFor={reason.reason_id}>{reason.client_reason}</label>
</div>
)
})
return (
<div className='cancel-survey'>
<form className='cancel-survey-form'>
{ reasons }
<div className='form-row'>
<input
type='radio'
id='other-option'
name='reason'
onChange={() => this.setReason(this.otherReason)} />
<label htmlFor='other-option'>
Other reason
</label>
</div>
{ this.state.reason.reason_id === 70 &&
<div>
<input
className='valid'
type='text'
id='other-option'
name='other-text'
placeholder="placeholder"
onChange={this.setOtherReasonText} />
</div>
}
<div className='button-row'>
<button
disabled={!this.state.reason.client_reason}
className={btnClassList}
onClick={this.handleSubmit}>
<span>Submit</span>
</button>
</div>
</form>
</div>
)
}
}
export default CancelSurvey
disabled={
!this.state.reason.client_reason
||
(this.state.reason.client_reason === 'other' && !this.state.otherReasonText)
}
If you want to make sure otherReasonText is not just empty spaces, use otherReasonText: '' as initial state then check !this.state.otherReasonText.trim().
Do some conditional logic before like:
const reasonId = this.state.reason.reason_id;
const otherReasonText = this.state.otherReasonText;
const clientReason = this.state.reason.client_reason;
const shouldDisableButton = !clientReason || (reasonId === 70 && otherReasonText.length === 0)
...
<div className='button-row'>
<button
disabled={shouldDisableButton}
className={btnClassList}
onClick={this.handleSubmit}>
<span>Submit</span>
</button>
</div>
I have a CheckboxGroup component which takes in an options array prop and generates CheckboxInput components. On page load I make a call to an API which returns an array of pre-selected checkboxes (delivered to the value prop). Depending on the logged in user, this call can return an empty array or a selection of previously selected checkbox options.
The following code successfully takes the response of the API call and sets the relevant checkboxes to 'checked'. The issue I have is that this code doesn't allow me to make changes to the checkboxes after page load (clicking a checkboxes has no effect).
I think there is also some disconnect between the initial selectedCheckboxes state and the value of the API call but I read that setting props as initial state is an anti-pattern (e.g. selectedCheckboxes: props.value,).
export default class CheckboxGroup extends Component {
constructor(props) {
super(props);
this.state = {
selectedCheckboxes: [],
};
}
addCheckboxToSelected = (id) => {
if (this.state.selectedCheckboxes.includes(id)) {
// Remove checkbox from array and update state
const filteredArray = this.state.selectedCheckboxes.filter(item => item !== id);
this.setState({ selectedCheckboxes: filteredArray });
} else {
// Add checkbox to array and update state
this.setState({ selectedCheckboxes: this.state.selectedCheckboxes.concat(id) });
}
}
checkIfSelected = (checkboxValue) => {
const preSelectedCheckboxes = this.props.value;
let selectedBool = false;
preSelectedCheckboxes.some(function(object) {
if (object.id === checkboxValue) {
selectedBool = true;
}
return false;
});
return selectedBool;
}
render() {
const { label, name, options } = this.props;
return (
<div className="form-group form-inline">
<span className="checkboxgroup-heading">{label}</span>
<div className="form-group-container">
{options.map(object => (
<CheckboxInput
key={object.value}
name={name}
label={object.label}
onChange={this.addCheckboxToSelected}
value={object.value}
checked={this.checkIfSelected(object.value)}
/>
))}
</div>
</div>
);
}
}
This is the stateless CheckboxInput component
const CheckboxInput = ({ name, label, onChange, value, checked }) => {
return (
<div className="field form-group filter-input">
<input
type="checkbox"
id={value}
name={name}
value={value}
onChange={() => onChange(value)}
checked={checked}
/>
<label htmlFor={value} className="form-control">{label}</label>
</div>
);
};
Check the following code snippet. This might help. Let me know if you have questions.
const CheckboxField = ({checked, onChange}) => {
return (
<input type="checkbox" checked={checked} onChange={ev => onChange(ev.target.checked)} />
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
options: [{id: "1", checked: true}, {id: "2", checked: false}]
};
}
handleCheckboxChange(checked, option) {
const {options} = this.state;
var cOptions = [...options];
for(var i in cOptions) {
if(cOptions[i].id == option.id) {
cOptions[i].checked = checked;
}
}
this.setState({
options: cOptions
}, () => console.log(options));
}
render() {
const {options} = this.state;
return (
<div>
{
options.map(option => {
return (
<CheckboxField key={option.id} checked={option.checked} onChange={value => this.handleCheckboxChange(value, option)} />
)
})
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>