Changing an array inside an object using React Hooks - reactjs

I have a form that looks like,
const virtual_form = {
name: 'virtual',
address_info: [
{
name: 'a',
address: '',
}
]
}
I use this as a default state of my hook
const [virtualForm, setVirtualForm] = useState(virtual_form)
I just providing the user to modify the address field.
<div className="input-text-wrapper">
<TextField
value={virtualForm.address_info.address}
name="address"
onChange={(e) => handleAccessInfoChange(e, 'virtual')} />
</div>
like above.
However, in my handleAccessInfoChange,
const handleAccessInfoChange = (e, type) => {
console.log(e.target.name, e.target.value, type)
switch (type) {
case 'virtual':
setVirtualForm({...virtualForm, address_info[0]: [...virtualForm.address_info, address: value] })
}
}
I am getting a syntax error when I try to change the virtualForm. It says 'address' is not defined no-undef.
How can I make this to only affect the address correctly?

You're treating the object with address and name as an array, but you cant assign an array with this syntax [address: value]. Its looking for a variable called address not using it as a key.
Instead, map over it and modify the object at the correct index. I have no way to know which index, so I'll assume 0 as in the question:
setVirtualForm({
...virtualForm,
address_info: virtualForm.address_info.map((info, i) => {
if (i == 0) {
return {
...info,
address: value
}
}
return info
}
})

Related

Change Boolean value based on the previous state value

I have created the toggle function where it will change the Boolean value to false. And I am passing that handler function to button, now I am trying to achieve the same by using previous value, the problem I am facing here is I am having a mock data which will have the following structure {[{}]} inside one object I'll have an array inside that I'll have another objects. I have posted the mock and older implementation by selecting only one value from the mock, could any one guide me how to change the boolean value for the mock which I have. Thanks in advance.
const custDetail = {
customers: [
{
name: "Abc",
isCreated: true,
},
{
name: "bcd",
isCreated: true,
},
{
name: "Dec",
isCreated: true,
},
],
};
Code:
const [creatingCust, setCreatingCust] = useState([custDetail])
const custData = [...creatingCust]
custData[0].customers[0].isCreated = false
setCreatingCust(custData)
//trying to use prevState but I am getting undefined
const onClick = () => {
setCreatingCust(prevState => ({isCreated:!prevState.customers[0].isCreated}))
Shallow copy the state, and all nested state, that is being updated. I suggest using the customer name property to match the customer element in the customers array that you want to update. Use Array.prototype.map to create a new array reference.
I suggest also just storing custDetail in the creatingCust state. I don't a reason to nest it in an array.
Example:
const [creatingCust, setCreatingCust] = useState(custDetail);
const onClick = (name) => {
setCreatingCust(prevState => ({
...prevState,
customers: prevState.customers.map(
customer => customer.name === name
? {
...customer,
isCreated: !customers.isCreated
}
: customer
),
}));
};
If you must have creatingCust be an array the process is similar, but instead of shallow copying into a new object you shallow copy into a new array.
const onClick = (name) => {
setCreatingCust(prevState => [{
...prevState[0],
customers: prevState[0].customers.map(
customer => customer.name === name
? {
...customer,
isCreated: !customers.isCreated
}
: customer
),
}]);
};

Bind multiple form inputs to vuex store array

So I have this array in Vuex Store, and I want to bind it's fields to multiple inputs in a form. The way I manged to get this working is like this:
template:
<b-form-input id="CustName2"
type="text"
v-model="CustName2" :maxlength="50"
placeholder="Nombre">
</b-form-input>
<b-form-input id="CustAddr"
type="text"
v-model="CustAddr" :maxlength="50"
placeholder="Dirección">
</b-form-input>
<b-form-input id="CustPostCode"
type="text"
v-model="CustPostCode" :maxlength="10"
placeholder="Cod. Postal">
</b-form-input>
Computed:
computed: {
CustName2: {
get () {
return this.$store.state.orderproperties.CustName2
},
set (value) {
this.$store.commit('SetCustName2', value)
}
},
CustAddr: {
get () {
return this.$store.state.orderproperties.CustAddr
},
set (value) {
this.$store.commit('SetCustAddr', value)
}
},
CustPostCode: {
get () {
return this.$store.state.orderproperties.CustPostCode
},
set (value) {
this.$store.commit('SetCustPostCode', value)
}
}
}
store.js:
orderproperties: {
CustName2: '',
CustAddr: '',
CustPostCode: ''
}
The thing is, now I need to add 5 more properties (5 more fields to the form), and I feel like I could be getting a single computed property as an array, and then bind this to each field in the form; instead of creating a single computed property for each field. The problem is that the setter will not bind each array element to each input. Any ideas on how to refactor this? Right now, for each field I need a computed property, a Store mutation and a Store getter for each field.
one of approaches:
In store.js add universal mutation
import Vue from 'vue'
export const mutations = {
updateProp: (state, payload) => {
const { prop, value } = payload
Vue.set(state.orderproperties, prop, value)
},
}
in methods add
methods {
onChange(prop, value) {
this.$store.commit('updateProp', {prop: prop, value: value})
},
getValue(prop) {
return this.$store.state.orderproperties[prop]
}
}
in template
<b-form-input id="CustName2"
type="text"
#change="onChange('CustName2', $event)"
:value="getValue('CustName2')"
:maxlength="50"
placeholder="Nombre">
<b-form-input id="CustAddr"
type="text"
#change="onChange('CustAddr', $event)"
:value="getValue('CustAddr')"
:maxlength="50"
placeholder="Dirección">
...

How to update a Textfield that has a value from an array of objects state variable in React?

In react I am trying to update a rendered mapped array of objects in Textfields with the value set to the objects value and then also be able to update/change the value in the Textfield and corresponding state value. Currently the array of objects is correctly being mapped and displayed with the objects value, however when trying to change the value within the TextField, nothing changes in the display and the console logs result in only changing the last letter of the value. It seems as though because I need the Textfield to start with a value, that value keeps the old value due to the data being rendered with a map or whether I am updating an array of objects wrong? Although in this example an array of objects is not needed, it applies to my component that does need it.
The following is some example code to demonstrate:
import React, { useState} from 'react';
const Test = () => {
const [data, setData] = useState([
{
num: 1,
name: 'hello'
},
{
num: 2,
name: 'world'
},
{
num: 3,
name: 'test'
},
]);
const handleChange = e => {
const { name, value, id } = e.target;
setData(data[id].name = value)
console.log(value)
}
return (
<div>
{
data.map((_itm, index) => (
<TextField key={index} value={_itm.name} onChange={handleChange} name='name' id={index.toString()}/>
))
}
</div>
)
}
So 3 textfields will be displayed with the values, hello, world, and test. When trying to edit the textfields, the values do not change.
Any and all help is appreciated, thank you.
In the state hook, data is set to be an array. So you should always pass in an updated copy of that array whenever calling setData.
const handleChange = e => {
const { value, id } = e.target;
// Make a shallow copy of the current `data`.
const newArray = [...data];
// Update the changed item.
newArray[id] = {
...newArray[id],
name: value
}
// Call setData to update.
setData(newArray);
console.log(value);
}
I had the same problem and the code above didn't work. I did it a little differently and wrote a code that works 100%, no matter how you name the key in the object
const changeHandler = (e) => {
const { id, name, value } = e.target
const newArray = [...data]
newArray[id][name] = value
setForm(newArray)
}

react-select component options are not applied

I'm trying to give options props programmingly, but options do not appear.
I'm getting data from graphql server by using react-apollo-hooks, and try to give some data to options of react-select.
I checked data and they were fine. However, using data doesn't work (Select component shows "No Options"), while declaring array works.
I checked that I properly passed variables between files, and received right data.
Here's my code:
Conatiner.js
const generated_list = [];
const listGenerator = () => {
data.sample.map( single => {
const temp = { label: single.A, value: single.B };
generated_list.push(temp);
}
}
where data is
data.sample = [ { A: '1', B: '1', C: '1' }, { A: '2', B: '2', C: '2' } ]
Presenter.js
<Button onClick={() => listGenerator()} text={'Get Lists'} />
<Select options={generated_list} />
But following code worked as I expect.
const declared_list = { [label: '1', value: '1'],
[label: '2', value: '2'] };
...
<Button onClick={() => listGenerator()} text={'Get Lists'} />
<Select options={declared_list} />
I thought generated list differs from declared list, so I compared them by console log.
console.log(
typeof generated_list,
generated_list`,
JSON.stringify(generated_list),
typeof declared_list,
declared_list`,
JSON.stringify(declared_list),
JSON.stringify(generated_list) === JSON.stringify(declared_list),
);
All things were same, and === operator returned true.
I don't know what to check next, so any helps would be much appreciated.
As suggested by Sathish Kumar in the comments, changes on the array do not affect React to trigger a rerender of the component.
You should move the array into the state and update the array using this.setState (or if you are using a functional component, use Reacts new Hooks).
class MySelect extends Component {
state = {
options: [],
}
populateArray = () => {
var newOptions = [];
/* Populate the `newOptions` array */
this.setState({ options: newOptions });
}
render() {
return (<>
<Button onClick={this.populateArray}>Click me</Button>
<Select { ... } options={this.state.options} />
<>);
}
}

React select to render values from array

Using https://github.com/JedWatson/react-select. I have an method that will loop through an array that I would like to input into the value for react-select picker. Any ideas why const value is invalid?
renderList (thelist, i) {
const { selectedOption } = this.state;
console.log(thelist);
// this throws me error
const value = {
value: {thelist.name},
label: {thelist.name}
}
return (
<div>
<Select
name="form-field-name"
onChange={this.handleChange}
value={value}
/>
</div>
);
You don't need the curly braces for the object values. Just do:
const value = {
value: thelist.name,
label: thelist.name
}
You only use curly braces when declaring an object or when you need to tell React to interpret something inside render() as plain JavaScript instead of a string.
However, you probably want to define an options prop too to your select component. value only gives the dropdown a selected value, but options is what actually defines... well, the options. The options can be multiple, so we define them in an array of objects.
So do:
options={[
{
value: thelist.name1,
label: thelist.name1
},
{
value: thelist.name2,
label: thelist.name2
}
]}

Resources