React dynamic add select input field - reactjs

I am trying Create a select field dynamically. When clicking the add button, it will dynamically add select fields. I have and issue where the values don't change.
My code can be viewed on codesandbox:
https://codesandbox.io/s/react-select-field-dynamically-uo4dy?file=/src/App.js

Take a look at my changes:
Forked sandbox
// I added a 3rd argument for the name:
const handleRoomChange = (option, index, name) => {
const value = option.value; // you had this as 'const { value } = option.value' which dereferences value twice
console.log(value);
const list = [...roomInputs];
list[index][name] = value; //you had this as list[index][value]
setRoomInputs(list);
};
// onChange passes the name as 3rd argument to handleRoomChange
<Select
name="boardBasic"
placeHolder="Board"
value={options.value}
onChange={option => handleRoomChange(option, i, "boardBasic")}
options={options}
styles={{
menu: (provided) => ({ ...provided, zIndex: 9999 })
}}
/>

You have three problems that I can see.
First, you only have a handleRoomChange function that is trying to handle both room changes and board changes. You should probably have a handleBoardChange function, as well.
Second, if you output option to the console inside handleRoomChange, you will notice that option.value is a number. So, when you proceed to set list[index][value] to value, what you're saying is that you want (for example) roomInputs[1][1] to be 1. roomInputs[1] has no 1 property, which is why you will end up with the wrong properties inside your object on submission. You need to be setting roomInputs[1].roomType to value, instead (and the boardBasic property in your handleBoardChange method, when you write it).
Third, you are trying to use object destructuring to assign object.value to your value variable...but you're trying to destructure object.value instead of destructuring object. For this reason, value is undefined every time you run the function. Replace the assignment with const { value } = option; and you will start getting defined values back.

Related

setState mutates the object reference

I am getting an object from parent component and setting to state. In child component I am updating the state, but the parent reference object values also changing instead of only state changes.
Parent Component has a huge object,
obj = {
values: {
per1: { name: "rsk" },
per2: { name: "ssk" },
}
}
Child Component:
const ChildComponent = ({obj}) => {
const [inp, setInp] = useState(obj.values);
const onChange = useCallback(({target}) => {
setInp((prev) => {
const nD = { ...prev };
//k1, k2 comes from some logic
nD[k1][k2] = target.value;
return nD;
})
}, []);
return (
Here the "inp" state is looped by objects and keys in text box to build a html form
)
}
Here the question is, why the core obj.values also changing on onChange setInp time. I dont want to disturb the obj.values untill i submit the form.
Because before submit the Form, I need to validate,
obj.values are equal or not to inp state values
Any idea on this.
The original object is changing because in JS, when you pass an array or an object in such a way, you are actually passing a reference to the original object/array.
Meaning that any changes made to the reference, will also affect the original.
In-order to avoid using the reference, you can copy the object/array and work with the copy instead.
There are a few ways of doing this, the simplest IMO is using the spread syntax.
Example:
const ChildComponent = ({obj}) => {
const [inp, setInp] = useState({...obj.values});
...
}
What we do here is "spread" the contents of obj.values into a new object, thus creating a new object and avoiding using the reference.
Note that the spread syntax only makes a shallow-copy, this isn't necessarily an issue unless you have some complex object which contains some other nested objects within it.
If you do need to perform a deep-copy, one simple way of doing it is via JSON methods.
Example:
const clone = JSON.parse(JSON.stringify(original));
First, this obj variable is incorrect because per1 is defined as object and object consist of key and value, but it is like a string array so please check that, and below is solution
In Child Component you should create a copy of that obj variable
const [inp, setInp] = useState(Object.assign({}, obj.values));
Bz if you set inp as the same variable it will pass the address of that variable so for that you need to create the copy and then you can change that inp.

How to update a boolean value of an element that its inside an array of objects in react using hooks

I have the following array:
const [notifications, setNotifications] = useState([
{
"idNotification": 1,
"message": "Message number 1",
"hideNotification": false,
},
{
"idNotification": 2,
"message": "message number 2",
"hideNotification": false,
}
])
what i want its to loop through this array to show each notification, and if I click on the X button (the X button appear with the prop "toggle") then i want that the notification value "hideNotification" becomes true, and therefore stop showing up on the list
{
notifications?.map((tdata, index) => (
tdata.hideNotification === false ?
<div key={index}>
<Alert
color='primary'
isOpen={!tdata.hideNotification}
toggle={() => {
// I know this doesn´t work
tdata.hideNotification = true;
}}>
{tdata.message}
</Alert>
</div> : null
))
}
I've seen other posts with similar question but with string values. I don't really know how to apply that to a boolean value
Hi. Please try this:
<Alert
color='primary'
isOpen={!tdata.hideNotification}
toggle={() => {
setNotifications((prevEl) => {
let ind = prevEl.findIndex(
(notification) => notification.idNotification === tdata.idNotification
);
notifications[ind].hideNotification = true;
return [...prevEl];
});
}}>
{tdata.message}
</Alert>
When you want a loop on an array and return the element and sometime not, map is not advised.
You should use filter.
You can filter your list on "hideNotification" by doing const
notificationsToDisplay = notifications?.filter((tdata, index) => {
return tdata.hideNotification === false
})
Now you can map on notificationsToDisplay array. Since it contains only the elements having hideNotification property set to true, you'll never see the ones having the property set to false.
Also to set you hideNotification you have to use a state, you can't modify it directly using an assignation. Otherwise your component won't rerender, and you will not see any change.
You shoud use setNotifications()
Since you know the index or even the id of the element you want to modify you just need to find your element in the notifications array using findIndex
and then update the nofications array using setNotifications()
Ok, #Sardor is somewhat-correct in his answer, but he is missing explanation and also some of it is incorrect/can be simplified. I tried to edit, but the edit queue was filled.
First, #Sardor is correct in using the setNotifications setter in the toggle method. You do want to set the state, not the object's property value, like the OP suggests with a comment saying it didn't work. #Sardor uses the findIndex to get the appropriate object in the notifications array, but you can simply use the tdata.idNotification to give you the mapped ID. He also tries to edit the notifications state directly inside the setNotifications method which is incorrect.
The point of having the previous state in the params of the setter method; e.g.
setNotifications((previousNotificationState) => ...)
is to use the previous state (at the time of running the setter) to mutate it knowing the previous state has accurate values.
TLDR; React makes it easy to keep track and edit the state, so don't try to make it hard. You should have a setter edit the state which would cause a re-render, here is an example:
{
notifications?.map((tdata, index) => (
hideNotification === false ?
<div key={index}>
<Alert
color='primary'
isOpen={!hideNotification}
toggle={() =>
setNotifications((prevEl) => Array.from(prevEl)
.map((copyTData) => copyTData.idNotification === tdata.idNotification
? { ...copyTData, hideNotification: true }
: copyTData)
)}>
{message}
</Alert>
</div>
: null
)
)
}
Notice how I:
used Array.from() to copy the previous state and then mutate it
The above use of Array.from() to copy the state was something a well respected colleague once told me was good practice.
Last (personal) note. index may be a unique identifier for the key property, but I'd argue you almost always have something on each iteration, besides the index, that could be unique. In this case the idNotification is unique and can be used as the key instead so the index property doesn't clutter the mappings parameters. Indexes mean nothing in an array when the array changes frequently.

How to use react-select-table's rows as options to react-select's input field

I have an input field that should accept multiple inputs from options that are already set. - This input field needs to appear like tag input fields so I used the React-select library:
The set options are going to come from a react-bootstrap-table so I disabled the built-in dropdown in the react-select input field.
The problem is that I cannot remove the selected items by deselecting the row from the react-bootstrap-table.
I think I am doing something wrong on how I'm passing the values between the input field and the table but can't figure it out.
Here is the codesandbox
The problem is you try to compare two Objects, however there is no generic means to determine that an object is equal to another.
You could JSON.stringify them both and compare but thats not reliable way.
Instead, you can filter the array by object key, lets say label.
In this case, your function should look like this:
const setSelected = (row: any, isSelect: any, rowIndex: any, e: any) => {
if (isSelect) {
setSelectProducts([...selectedProducts, row]);
} else {
const newArray = selectedProducts.filter(
(item) => item.label !== row.label
);
setSelectProducts(newArray);
}
};
Here is codesandbox to preview
#Edit
If you wanna do something onClear
onChange={(selectedOption, triggeredAction) => {
if (triggeredAction.action === 'clear') {
// Clear happened
}

How to prevent User from creating duplicate values using React-Select Creatable?

I am using react-select' Creatable to make a dropdown and allow user to create new item to the list.
This is what I have:
<Creatable
name="form-field-name"
value={this.props.selectWorker}
options={this.props.selectWorkers}
onChange={this.props.handleSelectWorker}
/>
Right now user can create new name even though it already exist, creating duplicates like shown below.
I saw that there is an option called isOptionUnique on react-select site.
Searches for any matching option within the set of options. This
function prevents duplicate options from being created. By default
this is a basic, case-sensitive comparison of label and value.
Expected signature: ({ option: Object, options: Array, labelKey:
string, valueKey: string }): boolean
I have not been able to use it. I have tried isOptionUnique=true, isOptionUnique={options:this.props.workers}, but I got Creatable.js:173 Uncaught TypeError: isOptionUnique is not a function error.
I can't find an example for isOptionUnique, what is the best way to filter react-select to prevent duplicates using Creatable?
It's expecting a function
isOptionUnique(prop) {
const { option, options, valueKey, labelKey } = prop;
return !options.find(opt => option[valueKey] === opt[valueKey])
}
Don't forget to add it to your component instance
isOptionUnique={this.isOptionUnique}
This can also be achieved using the isValidNewOption prop.
isValidNewOption = (inputValue, selectValue, selectOptions) => {
if (
inputValue.trim().length === 0 ||
selectOptions.find(option => option.email === inputValue)
) {
return false;
}
return true;
}
you define a function taking three parameter of the inputValue you typed in, the selectValue if a value is selected and the existing options as selectOptions.
The function should return true or false dependent on what conditions you want a new option to be valid. this will prevent addition of duplicates.
In the case above we prevent adding of new options if there is no text or if the email already exists in the available options

Property values empty in this.props

I have the following Component that I want to render (
https://github.com/sbycrosz/react-native-credit-card-input/blob/master/src/CreditCardInput.js). Its working fine however I want to assign default card field values e.g. number, using the values props defined.
<CreditCardInput
gooble={{number: 4111111111111111}}
values={{number: 4111111111111111}}
requiresName
requiresCVC
requiresPostalCode
validatePostalCode={validatePostalCode}
cardImageFront={cardFront}
cardImageBack={cardBack}
labelStyle={addCardStyle.label}
inputStyle={addCardStyle.input}
validColor={"black"}
invalidColor={"red"}
placeholderColor={"darkgray"}
onFocus={this._onFocus}
onChange={this._onChange.bind(this)}/>
The actual CreditCardInput.js file should deconstruct these values and pre-populated the fields according to the source code in the link above:
const {
cardImageFront, cardImageBack, inputContainerStyle,
values: { number, expiry, cvc, name, type }, focused,
allowScroll, requiresName, requiresCVC, requiresPostalCode,
cardScale, cardFontFamily, cardBrandIcons,
} = this.props;
<CreditCard
...
number={number}
...
However when i debug on CreditCardInput.js during render I can see that the this.props contains values property, however this is an empty object (instead of containing the number value defined). This seems very odd behaviour. I have tested with other props passed through (E.g: gooble={{number: 4111111111111111}}) and it is populated correctly in this.props even though its exactly the same, except for the names.
So in CreditCardInput.js when I change the source code from above to using the prop gooble it works:
const {
cardImageFront, cardImageBack, inputContainerStyle,
gooble: { number, expiry, cvc, name, type }, focused,
allowScroll, requiresName, requiresCVC, requiresPostalCode,
cardScale, cardFontFamily, cardBrandIcons,
} = this.props;
<CreditCard
...
number={number}
...
However why isnt it working with this.props.values/ why is this.props.values an empty object.

Resources