React - Fill array in state with input value - javascript

I have an empty link array and three input fields on my page.
I want to get the values of all three input fields in one array in my state variable link[] so that I can send a link array to my DB.
Like you see I am using semantic ui but for the input fields there is just the onChange function I can use
How can I do that?
export default class Dashboard extends Component {
constructor() {
super();
this.state = {
link : []
};
render() {
return (
<Grid style={{ height: "100%" }}>
<Grid.Column width={6} className='dashboardCard'>
<Card fluid>
<Card.Content >
<Card.Header>Allgemeine Künstler-Daten</Card.Header>
<Card.Description>
"Strange Fruit" is a song performed most famously by Billie
Holiday, who first sang and recorded it in 1939.
</Card.Description>
</Card.Content>
<Card.Content extra textAlign="center">
<Form onSubmit={this.handleSubmit} >
<Input
fluid
label="Social Link 1"
placeholder="www.twitter.com/artist"
style={{ margin: "1rem" }}
width={6}
name="link"
value={this.state.link}
onChange={this.handleInputChange}
/>
<Input
fluid
label="Social Link 2"
placeholder="www.soundcloud.com/artist"
style={{ margin: "1rem" }}
width={6}
name="link"
value={this.state.link}
onChange={this.handleInputChange}
/>
<Input
fluid
label="Social Link 3"
placeholder="www.facebook.com/artist"
style={{ margin: "1rem" }}
width={6}
name="link"
value={this.state.link}
onChange={this.handleInputChange}
/>
<Dropdown
<Form.Button positive
value="Submit">Save</Form.Button>
</Form>
</Card.Content>
</Card>
</Grid.Column>
<Grid.Column columns={5} />
</Grid>
);
}

I would recommend not storing the links in an array but as separate values with distinct keys in your component's state. That way you'll know which link to update if the user changes something. Just have keys in the state that correspond to inputs that are rendered, using the <input> name attribute. It's also convenient to wrap all of the data from the form in a formOptions key in the state in case you have other state data.
You can do something like this:
constructor() {
super();
this.state = {
formOptions: {
link1: '',
link2: '',
link3: ''
}
};
}
handleInputChange = event => {
const { name, value } = event.target;
const { formOptions } = this.state;
formOptions[name] = value;
this.setState({ formOptions });
}
Then just give your inputs the name attributes link1, link2, and link3.
Then in your handleSubmit method, you can convert the separate links into a single array if that's how you need to send them.
Note that the method handleInputChange needs to be written as an attribute assigned to an arrow function for binding this to the component so that the state can be accessed. Alternatively you can just define handleInputChange normally and then in each input's onChange put this.handleInputChange.bind(this), but I find the first way a bit cleaner.

Related

More generic way to set an objects value within React handleChange

I have a component in React, I need to take a value from the radio button being checked and then set that value within an object in my Formik values. If a different radio button is selected within the group I need to set the previously selected one to false, is there a standard way of doing this? I'm using an object within my Formik values as the field holds a date as well as the attribute from the radio button, you can see where I place the date into the object using handleTime, so I can't just null the field and place the new item in.
I'm currently doing the following in my component to update the Formik time_frame value.
import React, {Component} from 'react';
import {Col, Row} from 'react-bootstrap';
import Radio from "./radio";
export default class EventTimeFrame extends Component {
state = {
eventTimeFrame: [
{id: 1, value: "one_off", label: "ONE OFF", checked: false},
{id: 2, value: "recurring", label: "RECURRING", checked: false},
]
}
handleOccurance = value => {
let timeCopy = {...this.props.values.time_frame}
if (value.target.checked) {
if (value.target.value === "one_off") {
timeCopy[value.target.value] = true
timeCopy["recurring"] = false
} else {
timeCopy[value.target.value] = true
timeCopy["one_off"] = false
}
}
this.props.onChange("time_frame", timeCopy)
this.setState(prevState => ({
eventTimeFrame: prevState.eventTimeFrame.map(
el => el.value === value.target.value ? {...el, checked: true} : el
)
}))
};
handleTime = value => {
let timeCopy = {...this.props.values.time_frame}
timeCopy["start"] = new Date(value.target.value);
this.props.onChange("time_frame", timeCopy)
};
render() {
return (
<div>
<Row>
<Col>
<h4 className="ui centered question-header text-center">ONE OFF OR RECURRING EVENT?</h4>
</Col>
</Row>
<Row>
{
this.state.eventTimeFrame.map((timeFrame) => {
return (
<Radio name="time_frame" key={timeFrame.value}
onChange={this.handleOccurance} checked={timeFrame.checked} {...timeFrame} />
)
})
}
</Row>
<Row>
<Col>
<h4 className="question-header date-text">PLEASE ENTER THE FIRST DAY OF YOUR EVENT</h4>
</Col>
<Col>
<input type="date" className="form-control date" name="start"
onChange={this.handleTime}/>
</Col>
</Row>
</div>
)
}
}
I feel like there has to be a standard way of dealing with things like this
You're correct by suspecting there's a simpler way 😃 Some feedback:
Don't store the eventTimeFrame in state, a constant will do.
The standard way of using radio input values in Formik is to define one value in initialState for the entire group. Formik will set its value to the selected option.
Storing all options in values would have been fine if you were using checkboxes instead of radio buttons.
You wrote your own custom onChange handlers, but it would be simpler to just use Formik's provided onChange handlers. I only use my own handler if I have to, for example when formatting a value before setting it with setFieldValue()
See below:
Live Demo
// outside of component
const timeFrameTypes = [
{ id: "one_off", label: "ONE OFF" },
{ id: "recurring", label: "RECURRING" }
];
// in component's render function:
<Formik
initialValues={{
time_frame: {
type: "",
start: ""
}
}}
onSubmit={async (values) => {
// do something with values
}}
>
{({ handleSubmit }) => {
return (
<form onSubmit={handleSubmit}>
<div>
<Row>
<Col>
<h4 className="ui centered question-header text-center">
ONE OFF OR RECURRING EVENT?
</h4>
</Col>
</Row>
<Row>
{timeFrameTypes.map((timeFrameType) => {
return (
<Field
key={timeFrameType.id}
component={Radio}
name="time_frame.type"
id={timeFrameType.id}
label={timeFrameType.label}
/>
);
})}
</Row>
<Row>
<Col>
<h4 className="question-header date-text">
PLEASE ENTER THE FIRST DAY OF YOUR EVENT
</h4>
</Col>
<Col>
<Field name="time_frame.start">
{({ field }) => <input type="date" {...field} />}
</Field>
</Col>
</Row>
</div>
<br />
<button type="submit">Submit</button>
</form>
);
}}
</Formik>

form response values always give off undefined, trying to push to backend eventually, tried it 2 ways

Okay so I've attempted this 2 different ways and both give me a value of "undefined" instead of the answer that the user puts into the form, I'm using material UI forms if that helps.
The 2 ways I did is:
1. I have a button at the end that submits, which is the preferred way I want to do it and then display the data after.
2. I'm trying it as the user types so using onChange and it'll update on the UI as the user types.
Neither ways have been working because even when I submit or type I get console.log saying "undefined" each time, so I don't even know if I'm pulling results.
My end goal is to get the form data into variables and then upload those into the backend (to use on other pages of the app) and from the backend pull the information to display on this page so the user can see their information.
Any help is appreciated, thank you!
class AccountPage extends React.Component {
constructor () {
super()
this.state = {
email: '',
socMed1: ''
}
}
handleSubmit(event){
alert("test")
}
handleChange (event) {
this.setState( {[event.target.name]: event.target.value });
console.log(event.name)
}
render() {
return (
<div>
<h1>
{this.email}
</h1>
<form action="/" method="POST" onSubmit={(e) => { e.preventDefault(); this.handleSubmit(); } }>
<TextField
id="standard-name"
style={{ margin: 15 }}
label="Name"
margin="normal"
name="email"
value={this.state.email}
onChange={event => this.handleChange(event)}
/>
<TextField
id="standard-name"
style={{ margin: 15 }}
label="Location"
margin="normal"
/>
<TextField
id="standard-select-socialmedia"
select
style={{ margin: 15 }}
label="Select"
SelectProps={{
MenuProps: {},
}}
helperText="Please select your main social media"
margin="normal"
name = "socMed1"
value={this.state.socMed1}
onChange={event => this.handleChange(event)}
>
Assuming you have submit button at the bottom,
something like (you can use material ui one)
<button onSubmit={(event) => this.handleSubmit(event)} />
and try adding this to your alert
alert(`email: ${this.state.email}, socMed1: ${this.state.socMed1}`)
then you will see what user typed in field
Hope it helps!
a functional component with form. checkout Just do some research this is just HTML functionality. if you want to use class component you can go ahead but just use "this.handleSubmit in onSubmit form would work
import React from "react";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
const Demo = () => {
const handleSubmit = event => {
const formData = new FormData(event.target);
//does not resets the form
event.preventDefault();
// accessing each values
for (var [key, value] of formData.entries()) {
console.log(key, value);
}
// posting you values to api
fetch("/api/form-submit-url", {
method: "POST",
body: formData
});
};
return (
<React.Fragment>
<form onSubmit={handleSubmit}>
<div style={{ display: "flex", flexDirection: "column" }}>
// names are important because this will be treated as a key value pair in handle submit.
<TextField id="standard-basic" name="user" label="user" />
<TextField id="standard-basic" name="email" label="email" />
<TextField id="standard-basic" name="password" label="password" />
<Button type="submit" variant="contained">
Submit
</Button>
</div>
</form>
</React.Fragment>
);
};

How to prevent setState changing input field?

I am using ant design form and trying to prevent clearing input after I check a checkbox.
Here is the code :
this.state = {
externalOptionsArray: [],
}
// onClick function
optionOnChange = (e, index, array) => {
const { externalOptionsArray } = this.state
let externalOptionCurrentState = externalOptionsArray
externalOptionCurrentState[index].is_enabled = !externalOptionCurrentState[index].is_enabled;
this.setState({
externalOptionsArray: externalOptionCurrentState
})
}
// Components
<Form onSubmit={this.onSubmit}>
<FormContainerWithDescription
direction="vertical"
title="Product"
description="Product Information">
<FormItemRow>
<Col span={24} style={colStyle}>
<FormItem label={'Product name'} colon={false} style={{ marginBottom: 0 }}>
{getFieldDecorator('title', {
rules: [
{ required: true, message: 'name is required' },
],
})(<Input />)}
</FormItem>
</Col>
</FormItemRow>
<FormItemRow>
<Col span={24} style={colStyle}>
<FormItem label={'external_options'} colon={ false } style={{ marginBottom: 0 }}>
{ externalOptionsArray.map((option, index, array) => {
return (
<Col style={{ float: 'left', width: '50%' }} key={option.name}>
<Row>
<Checkbox defaultChecked={option.is_enabled} onChange={() => this.optionOnChange(index, array)}>{option.name}</Checkbox>
</Row>
</Col>
)
})}
</FormItem>
</Col>
</FormItemRow>
</FormContainerWithDescription>
</Form>
And here is the image for better understanding
The problem is when I input a text in a input field and click one of checkboxes, input field automatically cleared up, it goes blank input field again.
I believe this is happening due to setState inside of optionOnChange function. Whenever I click one of checkboxes, setState takes place and it rerenders the DOM.
So I used e.preventDefault() and e.stopPropagaion inside of Checkbox component like this below.
<Checkbox defaultChecked={option.is_enabled} onChange={() => this.optionOnChange(index, array)} onClick={e => e.preventDefault()}>{option.name}</Checkbox>
// and
optionOnChange = (e, index, array) => {
e.preventDefault()
e.stopPropagation()
const { externalOptionsArray } = this.state
let externalOptionCurrentState = externalOptionsArray
externalOptionCurrentState[index].is_enabled = !externalOptionCurrentState[index].is_enabled;
this.setState({
externalOptionsArray: externalOptionCurrentState
})
}
But neither of them work
How can I make Checkbox works without rerender the input field?
You need to make it a controlled component. If you set the value of the Input component using a state variable then it remains even after state change.
So looking at your code above, you may need to have another variable like productName in the state and a onChange event handler to set the state when the input is changed
this.state = {
externalOptionsArray: [],
productName: '',
}
onChange = (e) => {
this.setState({productName: e.target.value});
}
And pass this to the Input like
<Input value={this.state.productName} onChange={this.onChange} />
You can store product's name in state and change it when input changes.
this.state = {
externalOptionsArray: [],
productName: '',
}
then define onChange function where you handle updating the state, like you did in optionOnChange()
and pass it to your Input component
<Input onChange={this.onChange} />

Select more than one radio button that are mapped into different radio groups - React

I am using react and have mapped some data into their own groupings. Once they have been mapped into a group I am then mapping the items into the group of which they came from.
The problem I am getting is I can still only choose one radio button in the entirety when I should be able to choose one from each group. I am using material-ui for <Radio Group>.
https://codesandbox.io/s/9lz7q7557w
Mapping the newly sorted keys(lodash) into a render:
{data.length > 0 ?
Object.keys(groups).map((item, index) => this.renderPlayerListItem(item, groups))
:
<div className="text-center">
NO RESULTS
</div>
}
Then mapping the group items into the individual returns:
<React.Fragment>
<h2>{item}</h2>
{groups[item].map((person, i) =>
<RadioGroup
aria-label="matches"
name="matches"
value={String(this.state.value)}
style={{ display: 'block' }}
>
<FormControlLabel
onClick={e => this.handlePlayerToggle(e)}
checked={this.state.checked}
key={String(person.Id)}
value={String(person.Id)}
control={<Radio color="primary" />}
label={
<div>
{person.firstName} {person.lastName}
</div>
}
/>
</RadioGroup>
)}
</React.Fragment>
https://codesandbox.io/s/9lz7q7557w - Something I put together as an example. Same outcome unfortunately. Any help would be appreciated. Welcome any alternatives to desired result if I have gone about it the wrong way.
UPDATE: I think the issue is the value. Each radio input has a value 1,2,3,4. I think it should be 1,2 and 1,2 in each group
Looking at your sandbox, the issue is with the state variable as value. This value is always being set once and is not separated group wise. This single variable is controlling all the Radio toggle in all the groups. You have to separate each group in an independent component with independent toggle state. So your new component would look something like this:
class RadioGroupComponent extends Component {
constructor(props) {
super(props);
this.handlePlayerToggle = this.handlePlayerToggle.bind(this);
this.state = {
value : _.head(props.items).Id,
}
}
handlePlayerToggle(e) {
this.setState({ value : e.target.value })
}
render() {
const { items } = this.props;
return (
<React.Fragment>
{items.map(persons, person => (
<RadioGroup
aria-label="matches"
name={groups}
value={String(this.state.value)}
style={{ display: "block" }}
>
<FormControlLabel
onClick={e => this.handlePlayerToggle(e)}
checked={this.state.checked}
key={String(person.Id)}
value={String(person.Id)}
control={<Radio color="primary" />}
label={
<div>
{person.firstName} {person.lastName}
</div>
}
/>
</RadioGroup>))}
</React.Fragment>);
}
}
lodash head will help to set the first item selected by default in each group.
And now each group will have its one state to be set hence making each group function independently. Hope it helps :)

Pass from parent to child onChange function which does not work

There is parent component which have handleChange function, I pass to the child that function.
And provide to the input fileds, then there is bug with input fileds, I am not able to change values at all.
ParentComp:
handleChange = (e, data) => {
if (data && data.name) {
this.props.setFieldValue(data.name, data.value)
if (data.name === 'pay_rate') {
console.log('PAY_RATE: ', data)
}
}
}
return (
<Grid.Column width={8}>
<Segment raised>
<Header>
<p style={{ fontSize: '1.2rem' }}>
Church Mutual Worker\'s Compensation Claim
<span style={{ float: 'right' }}>{`Claim #${props.claim.claimNumber}`}</span>
</p>
</Header>
<Form onSubmit={handleSubmit}>
// Here called is Child component
<EditStandaloneClaimDetails
claim={props.claim}
loading={loading}
handleChange={props.handleChange}
/>
</Form>
<Comment currentClaim={props.currentClaim} />
</Segment>
</Grid.Column>
)
ChildComponent:
const EditStandaloneClaimDetails = ({ handleChange, claim, loading, testChange }) => {
if (!claim || loading) {
return null
}
const { noticeOnly, recieveDate, accountNumber } = claim
return (
<Segment
raised
style={{
backgroundColor: '#F0F0F0',
}}
>
<h5>Claim Details</h5>
<Form.Group >
<Form.Field>
// CANNOT ENTER A NEW VALUE FOR DATE INPUT FILED
<label>Date Received</label>
<DateInput
name={'recieveDate'}
placeholder="Date received"
value={recieveDate}
onChange={handleChange}
style={{ width: '65%' }}
dateFormat={'MM/DD/YYYY'}
/>
</Form.Field>
</Form.Group >
Maybe problem is in this attribute value={recieveDate}?
You can handle it using call back function.
In your parent component replace below lines:
handleChange = (e, data) => {
//here you get the updated date
//add your logic
}
// Here called is Child component
<EditStandaloneClaimDetails
recieveDate = {'your date'}
claim={props.claim}
loading={loading}
handleChange={(event, data) =>this.handleChange(event,data)}
/>
In Child component add below function, that function is responsible to call back to parent component.
constructor(props){
super();
this.state = { recieveDate: prop.recieveDate };
}
HandleChange(event,value){
this.setState({ recieveDate})
this.props.handleChange(value,event.uid);
}
<DateInput
name={'recieveDate'}
placeholder="Date received"
value={recieveDate}
onChange={(event, value) =>this.HandleChange(event,data)}
style={{ width: '65%' }}
dateFormat={'MM/DD/YYYY'}
/>

Categories

Resources