How to get values and add them to dynamic form fields? - reactjs

I have created a form that can add fields dynamically(add more). I need to auto populate data that is taken through an id of a select field and add them into some input fields. problem is I am getting the expected result but not getting the value added to those specific data fields. It auto populated every dynamic field that is created relevant to its attribute name.
Here's my states
const [productSelectedList, setproductSelectedList] = useState([])
const [inputList, setInputList] = useState({
user_id: '',
agent_id: '',
tonnes_complete: '',
dockets_complete: '',
customer_id: '',
customer_address: '',
spreading_unit: '',
payment_method: '',
spread_completed_data: '',
spread_rate: '',
payment_status: '',
order_status: '',
order_list: [
{
docket_number: '',
operator_id: '',
product_id: '',
product_mix_id: '',
product_docket: '',
quantity: '',
quantity_rate: '',
spread_status: '',
driver_comments: '',
},
],
})
Here's my onchange and how i am selecting the data from an api
const handleChangeProductMix = (e, index) => {
const { name, value } = e.target
const list = { ...inputList } //<-- object, not array
list.order_list[index][name] = value
const product_mix_id = list.order_list[index][name]
axios.get(`api/edit_product_mix/${product_mix_id}`).then((res) => {
if (res.data.status === 200) {
setproductSelectedList(res.data.product_mix)
} else if (res.data.status === 404) {
swal('Error', res.data.message, 'error')
history.push('/products/productmix')
}
})
setInputList(list)
console.log(productSelectedList)
}
const handleChange = (e, index) => {
const { name, value } = e.target
const list = { ...inputList } //<-- object, not array
console.log(list)
list.order_list[index][name] = value
setInputList(list)
}
and here's my input field
<div className="col-lg-4 mb-2">
<div className="form-group">
<label className="pb-2">Product Mix Name</label>
<input className="form-control" type="text" name="product_docket" onChange={(e)=> handleChange(e, i)}
value={productSelectedList.product_docket}
placeholder="Select Customer Code"
/>
</div>
</div>
When the auto populated data is added and when i click add more the data is being duplicated and replaced with recently selected option.
here's how i add fields dynamically
const handleAddInput = (e) => {
e.preventDefault()
setInputList({
...inputList,
order_list: [
...inputList.order_list,
{
docket_number: '',
operator_id: '',
product_id: '',
product_mix_id: '',
product_docket: '',
quantity: '',
quantity_rate: '',
spread_status: '',
driver_comments: '',
},
],
})
}

You are directly modifying React state with these lines in handleChange and handleChangeProductMix:
const list = { ...inputList } //<-- object, not array
list.order_list[index][name] = value
You need to deep clone the inputList state instead, so that you are not directly modifying one of the values on a property of one of the objects in the array on inputList.order_list.
Here are a few different ways to do it:
JSON.parse:
const list = JSON.parse(JSON.stringify(inputList));
Spread syntax (like in handleAddInput):
const list = {
...inputList,
order_list: inputList.order_list.map(o => ({...o})),
};
A simple clone function:
/**
* This is a naive clone implementation, only meant to be used with
* plain objects/arrays and scalar values.
*/
function clone (value) {
if (typeof value !== 'object') return value;
if (value === null) return value;
if (Array.isArray(value)) return value.map(v => clone(v));
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, clone(v)]));
}
const list = clone(inputList);

Related

React JS Unhandled Rejection (TypeError): item.persistVariablesLimit.trim is not a function

I'm getting the following error:
React JS Unhandled Rejection (TypeError): item.persistVariablesLimit.trim is not a function
when I try to set a default value on the persistVariablesLimit.
Here is an image of the stack trace and console log:
Here's the code that generates it:
if (typeof item.persistVariablesLimit !== 'undefined') {
item.persistVariablesLimit = item.persistVariablesLimit.trim()
if (!item.persistVariablesLimit.match(/^[0-9]+$/)) {
formIsValid = false
errors['persistVariablesLimit'] = 'Must be numeric'
}
}
There is no problem with this field if the data is entered manually, even if it is entered, then deleted.
Also, if I don't set a default value, and don't enter anything into the field, it is saved successfully as an empty string.
Here's where it sets the default value (when the "FA" template is selected):
handleSelectTemplateChange = (event, { value }) => {
let item = { ...this.state.item }
item.template = value
var str1 = '' + item.template
if (str1.startsWith('SMA')) {
item.family = 'Safety-Machine-Analytics'
}
if (str1.startsWith('FA')) {
item.family = 'Field Analytics'
item.product = 'Field Analytics'
if (!item.persistVariablesLimit) {
item.persistVariablesLimit = 50;
}
if (!item.dataSourceLimit) {
item.dataSourceLimit = 50;
}
}
else {
item.persistVariablesLimit = "";
item.dataSourceLimit = "";
}
this.setState({ item })
}
This is the UI code for the template:
<Form.Select
fluid
label='Template'
options={this.state.templateOptions || []}
placeholder='Template'
name='template'
value={item.template}
required={true}
onChange={this.handleSelectTemplateChange}
/>
And for the persistVariableLimit field:
<Form.Input
label='Persist Variables Limit'
placeholder='Persist Variables Limit'
name='persistVariablesLimit'
value={item.persistVariablesLimit || ''}
onChange={this.handleChange}
required={false}
disabled={false}
error={this.state.errors['persistVariablesLimit']}
/>
This is an item originally retrieved from an API. It's initialized as follows:
emptyItem = {
fullName: '',
contact: '',
requester: '',
tag: '',
company: '',
companyId: '',
product: '',
expiration: '',
macsArray: '',
dashboardIDs: '',
typeId: '',
family: '',
systems: '',
fileName: '',
url: '',
attributesArray: [],
persistVariablesLimit: '',
dataSourceLimit: ''
}
constructor(props) {
super(props)
const { cookies } = props
this.state = {
item: this.emptyItem,
csrfToken: cookies.get('XSRF-TOKEN'),
fields: {},
errors: {}
}
...
}
Here's the API call:
if (this.props.match.params.id !== 'new') {
try {
const tmpLicense = await (await fetch(API_HOST + `/api/license/${this.props.match.params.id}`, { credentials: 'include' })).json()
this.setState({ item: tmpLicense })
} catch (error) {
this.props.history.push('/')
}
How should I be setting this default value? What is the issue here?
You’re setting the default values as numbers, .trim is a String method.
It should be:
if (!item.persistVariablesLimit) {
item.persistVariablesLimit = '50';
}
if (!item.dataSourceLimit) {
item.dataSourceLimit = '50';
}

add inputs dynamically react redux

hello I will ask this question again because I still can’t find a answer
I try to create a form similar to google form with react and redux
each question is represented by: QuestionCard
contains a title and can have several types (short question, long question, choice ,multiple, single choice ...)
I manage to add cardQuestion and delete them
my problem is that when I select single choice I want to give the user the possibility to add the number of choices he wants but it does not work
this is my reducer
const initialState = {
formId: '',
form: {
title: '',
description: ''
},
basicForm: {
fullName: '',
age: '',
saved: false
},
cardQuestion: [{
title: '',
choix: [],
type: '',
}],
error: "",
loading: false,
}
const addRequestReducer = (state = initialState, action) => {
switch (action.type) {
case 'SET_STATE':
return { ...state, ...action.payload }
default:
return state
}
}
i use a global state
and this how i try to add choices
1- i handle change of inputs (each choice) like this :
const { cardQuestion } = useSelector((state) => state.addRequest)
const choice = cardQuestion.map(e => e.choix)
const onChangeInput = (i, key, value) => {
console.log(value)
dispatch({
type: 'SET_STATE',
payload: {
cardQuestion: cardQuestion.map((elem, index) => {
console.log(i)
console.log(i === index)
if (i !== index) return elem
return { ...elem, [`${key}`]: value }
})
}
})
}
2- use the ChangeInptut like this
<div >{choice.map((elem, i) => <div key={i}>
<Input value={elem.choix} onChange={(e) => onChangeInput(i, 'choix', e.target.value)} />
</div>
3- button to add new input (choice)
<Button onClick={() => dispatch({
type: 'SET_STATE',
payload: {
cardQuestion: [...cardQuestion, { choix: '' }]
}
})} >Add</Button>
</div>
but it give me results like this :
and when i add a choice in question card1 it is also added in questioncard2
how can i resolve this , any help will be appreciated
This
cardQuestion: [...cardQuestion, { choix: '' }]
adds a new card without a title or type to the list of cards. If I understand you correctly, you want to add a new choice to an existing card, which will look something like
const newCards = [...cardQuestion]
newCards[currentCardPosition] = {...newCards[currentCardPosition]}
newCards[currentCardPosition].choix = [...newCards[currentCardPosition].choix, '' ]
but I don't see where currentCardPosition will come from

React state manipulation through spread operator

I have this initial state:
this.state = {
formInfo: {
name: '',
age: '',
height: ''
},
errorMessage: ''
}
When editing the form, the state should update and this is how I'm handling this at the moment:
handleInputChange = e => {
const { name, value } = e.target
this.setState({
...this.state,
formInfo: {
...this.state.formInfo,
[name]: value
}
})
}
Can you provide me of a better solution to manipulate the state, in case this process does not follow the industry standards?
I doubt if there's a more efficient way to archive this.
If you are asking about best practice then below is the one. Your code was fine. Only better to avoid dot notation's and provide default values.
handleInputChange = e => {
const {
target: {
name = '',
value = '',
} = {},
} = e;
const {
formInfo,
} = this.state;
this.setState({
formInfo: {
...formInfo,
[name]: value,
},
});
}

How to check that new input will be unique in react native ListView data-source

I need to check that new input which will be added to the dataSource is not contained in it already.
_handleSendButtonPress = () => {
const textArray = this.state.dataSource._dataBlob.s1;
// Here I need to check that this.state.inputValue is in textArray already
textArray.push(this.state.inputValue);
this.setState(() => ({
dataSource: this.state.dataSource.cloneWithRows(textArray),
inputValue: '',
}));
};
If I understand you correctly and textArray is indeed an array, this should work:
_handleSendButtonPress = () => {
const textArray = this.state.dataSource._dataBlob.s1;
!textArray.includes(this.state.inputValue) && (
textArray.push(this.state.inputValue);
this.setState(() => ({
dataSource: this.state.dataSource.cloneWithRows(textArray),
inputValue: '',
}));
)
};

How to add key name and key value from Form?

I'm working on a React app and want the form I'm working on to be able to add key name and key value to sensorProperties: {} (sensorProperties is a part of sensor object). I have to allow user to give a name for both, key and value.
this.state = {
sensor: {
name: '',
type: '',
position: '',
sensorProperties: {}
},
show: false,
key: '',
value: ''
};
I added two functions handleValueChange and handleKeyChange. When I invoke console.log(key, value), I see they have my input values, but how can I pass these values to my sensorProperties?
handleKeyChange = (event) => {
const nKey = event.target.value
this.setState({key: nKey})
}
handleValueChange = (event) => {
const nValue = event.target.value
this.setState({value: nValue})
}
<FormGroup>
<ControlLabel>Key</ControlLabel>
<FormControl required type="text" value={key}
onChange={this.handleKeyChange}/>
</FormGroup>
<FormGroup>
<ControlLabel>Value</ControlLabel>
<FormControl required type="text" value={value}
onChange={this.handleValueChange}/>
</FormGroup>
You can change the handlers and set the sensorProperties like this:
handleKeyChange = (event) => {
const nKey = event.target.value
const value = this.state.value;
const newSensor = { ...this.state.sensor };
const newSensorProperties = {
[nKey]: value, // dynamically set the key name
};
newSensor.sensorProperties = newSensorProperties;
this.setState({
sensor: newSensor,
key: nKey,
});
}
handleValueChange = (event) => {
const nValue = event.target.value
const key = this.state.key;
const newSensor = { ...this.state.sensor };
const newSensorProperties = {
[key]: nValue, // dynamically set the key name
};
newSensor.sensorProperties = newSensorProperties;
this.setState({
sensor: newSensor,
value: nValue,
});
}
Depends on your need, the method above will only set one key-value pairs, and it also accepts an empty string as the key of the sensorProperties. If it is not want you want, you can just add an additional checking for empty string. The main idea is to use the computed key names for the newSensorProperties and that should solve the problem.

Resources