react-native-multiple-select storing items selected on submit - arrays

I am using react-native-multiple-select and trying to create a dropdown menu that allows users to select multiple options and then logs the options they select into an array.
At the moment, my code is:
onSelectedItemsChange = selectedItems => {
this.setState({ selectedItems });
console.log('submit button was pressed')
};
render() {
const { selectedItems } = this.state;
return (
<View style={{ flex: 1 }}>
<MultiSelect
hideTags
items={items}
uniqueKey="id"
ref={(component) => { this.multiSelect = component }}
onSelectedItemsChange={this.onSelectedItemsChange}
selectedItems={selectedItems}
selectText="Pick Items"
searchInputPlaceholderText="Search Items..."
onChangeInput={ (text)=> console.log(text)}
altFontFamily="ProximaNova-Light"
tagRemoveIconColor="#CCC"
tagBorderColor="#CCC"
tagTextColor="#CCC"
selectedItemTextColor="#CCC"
selectedItemIconColor="#CCC"
itemTextColor="#000"
displayKey="name"
searchInputStyle={{ color: '#CCC' }}
submitButtonColor="#CCC"
submitButtonText="Submit"
/>
<View>
The problem is with the submit button. I only want to record the items selected once the user has pressed submit.
At the moment it logs that the button was pressed every time a new item is selected and that does not help with storing the items selected into another array.
Any help would be great.

You can do this to get an array with the item objects that are selected:
for(var i = 0; i < selectedItems.length; i++){
this.state.selectedItemsArray.push(this.state.gasOptions[selectedItems[i]])
}
console.log(selectedItems);
This should output the array of items that are selected with each item containing the unique key and the display name.

this.state.selectedItemsArray.push(listOfObject[0].id);
I noticed that the selectedItemsArray stores only the key so its an array of keys and not list of objects. Thus, if your key is id you want to push that to the array and not all the object.

I faced the same issue before. Now I fixed it.
Follow below steps:
Go to node_modules/react-native-multi-select/index.d.ts add the code
onSubmitclick: ((items: any[]) => void), inside export interface MultiSelectProps {}
Go to lib/react-native-multi-select.js add the code
onSubmitclick: PropTypes.func, inside the static propTypes ={}
Go to the function _submitSelection() and add the code inside it
const {selectedItems, onSubmitclick } = this.props; onSubmitclick(selectedItems);
Now you return your Multiselect tag add
onSubmitclick={(value1) => getSubmit(value1)}
capture your selected value with this function
const getSubmit = (value1) => { console.log('new submit value***', value1) }
I hope, It will helpful for someone.

Related

.filter() function creating loop in delete function - React

I've got a 'list' component, which allows you to add an item to a state array, and then display it from the state afterwards. These list items can then be removed by the user afterwards (or should be able to).
There's four state props in this component:
currentList: content in the input that's to be added to the list array
setCurrentList: what's used to change the content in the input
fullList: the full array list
setFullList: used to add the currentList content to the array, and removed
I'm using .filter() to create a copy of the state array, and then set the state afterwards in this function:
const deleteFromList = (e) => {
console.log("Delete button pressed")
console.log(e)
let fullList = props.fullListState
let setFullList = props.setFullListState
let filteredArray = fullList.filter(item => item)
setFullList(filteredArray)
}
However, every time I execute this function (i.e. when the delete button is pressed), it just creates a loop and the first two console.logs are just repeatedly done.
This is the full return function itself:
<>
<label className="setup-jobs-label">{props.label}</label>
<div className="setup-jobs-input-container">
<input className="setup-jobs-alt-input" type="text" onChange={onChange} value={props.currentListState} />
<button className="setup-jobs-add-button" onClick={addToList}>Add</button>
</div>
{ props.fullListState === [] ? null : props.fullListState.map(x => {
return <div className="setup-jobs-input-container" key={props.fullListState[x]}>
<p className="setup-jobs-input-paragraph">{x}</p>
<button className="setup-jobs-delete-button" onClick={deleteFromList(x)}>Delete</button>
</div>
}) }
</>
The important bit is the bottom conditional render, which checks to see if the state array is empty, and if so, not display anything. If it isn't, then it returns null.
Any advice would be appreciated - not sure what I'm doing wrong in the filter function.
In your onClick handler, you pass the result of the execution of deleteFromList, you should pass a reference to this function instead :
// note the '() =>'
<button className="setup-jobs-delete-button" onClick={() => deleteFromList(x)}>Delete</button>
See https://reactjs.org/docs/handling-events.html for more details about this.
Beside this, your filter logic does not seem right :
// this line only removes falsy values, but not the "e" values
let filteredArray = fullList.filter(item => item)
// you should implement something like this
let filteredArray = fullList.filter(item => [item is not "e"])
// this should work as we work on objects references
let filteredArray = fullList.filter(item => item !== e)

SweetAlert of React always delete/edit last item of a dynamic list

I'm using React in a list of dynamically items (added with an input and a button in a form) and each item has 2 buttons: to EDIT the name of item and to DELETE the item. I'm using the react-bootstrap-sweetalert library:
DELETE:
<SweetAlert
show={isModalDeleteOpen}
info
title="Delete item?"
onConfirm={() => handleDeleteItem(item.id)}
onCancel={closeDeleteModal}
>
I did it!
</SweetAlert>
EDIT:
<SweetAlert
show={isModalEditOpen}
input
showCancel
cancelBtnBsStyle="light"
title="Item's name"
placeHolder="Write something"
onConfirm={(response) => {
handleEdit(response, item.id)
}}
onCancel={closeModalEdit}
>
Change the item's name:
</SweetAlert>
Example:
> Item 1 - ButtonEdit - ButtonDelete
> Item 2 - ButtonEdit - ButtonDelete
> ...
The problem is that the last item is always deleted/edited, even if the delete/edit button for the first item is clicked. Debugging, I have seen that the "item.id" that reaches the function is the wrong one, but I don't know why. Can someone help me? Thanks!
PD: Both SweetAlerts are added into the creation of each 'li', so that there is modal for each 'li', but it doesn't seem to be the solution:
items.map((item) =>
> li key={item.id}
> item name
> edit button
> delete button
> SweetAlert (edit button)
> SweetAlert (delete button)
>/li
...
const handleEdit = (newValue, id) => {
const editToDoAction = {
type: 'edit',
payload: id,
name: newValue
}
dispatch(editToDoAction);
}
...
case 'edit':
return state.map( item => {
if (item.id === action.payload) {
item.name = action.name;
}
return item;
});
}
It seems you may be using the same boolean state to display all the alerts. Meaning the isModalDeleteOpen and isModalEditOpen are shared by all the alerts.
This would mean if isModalDeleteOpen is true, all the delete alerts would be displayed and probably the last one is stacked on top of all others.
The state of the component that would make sure that is the problem is not mentioned though so this is unfortunately a partial guess.
The solution would be to have a seperate state isModalDeleteOpen state for each alert

React Kendo Treeview scroll to item

I am using React Kendo Treeview UI. I want to try to scroll to the item that is selected in the tree. I found many examples for Javascript and JQuery but none for React version. I couldn't solve this problem by playing around with it.
Items in the tree are of type MyViewTreeModel. I have a selectOntree method that finds a node and set the selected to true. My problem is I want to scroll to that item.
export interface MyViewTreeModel {
text: string,
expanded: boolean,
employeeId : number,
treeId: number,
items?: MyViewTreeModel [],
selected: boolean
}
....
<TreeView
data={myData}
expandIcons={true}
onExpandChange={onExpandChange}
onItemClick={OnItemClick}
aria-multiselectable={false}
aria-label={'text'}
></TreeView>
....
const selectOnTree = (employeeId: number ) => {
let treeItem = recursivelyFindEmployeeInTree(myData[0], employeeId);
treeItem.selected = true;
forceUpdate();
}
}
myData is of type MyViewTreeModel .
One solution I tried : I added ref?: any to my model and tried treeItem.ref.current.focus(); in selectOnTree function, but ref was undefined.
Another solution I tried is adding this property to TreeView:
ref={component => treeViewRef.current = component}
Then tried this just to select the first 'li' tag in the TreeView:
if(!_.isNil(treeViewRef.current) ){
let domElement = ReactDOM.findDOMNode(treeViewRef.current);
let treeItemDom = domElement.firstChild.firstChild;
(treeItemDom as HTMLElement).focus();
}
This didn't work, it doesn't put the focus at that point.
I am thinking maybe I should define a custom itemRender that has a ref that I can find the offsetTop of it, but then there are more than one item, how can I create a different ref for each one? Or maybe a custom ItemRender that renders an input (with css I can make it look like a span) and then set autofocus to true if selected is true. Not sure if autofocus true make it scroll to that item.
This is the solution I could find to make it work:
Adding a reference to TreeView
let treeViewRef = useRef(null);
In return statement:
<TreeView
data={myData}
expandIcons={true}
onExpandChange={onExpandChange}
onItemClick={OnItemClick}
aria-multiselectable={false}
aria-label={'text'}
ref={component => treeViewRef.current = component}></TreeView>
2.Defined this function to scroll to a specific treeItem:
'k-in' is the className for each span that represent each item in the Kendo Treeview UI component.
const scrollToTreeViewItem = (treeItem: MyViewTreeModel ) => {
if(!_.isNil(treeViewRef.current)){
let domElement = ReactDOM.findDOMNode(treeViewRef.current);
let treeItemDoms = (domElement as Element).querySelectorAll('.k-in');
let domArray = [];
treeItemDoms.forEach((node) => {
domArray.push(node as HTMLElement);
});
let targetedDomElement = domArray.find((item) => {
return item.innerText === treeItem.text;
});
targetedDomElement.scrollIntoView();
}
}

React (Reakit): How to ask for confirmation, when toggling a checkbox?

I'm wondering, if there's a way to ask for confirmation with Reakit's checkbox. I'm using Reakit, since I found a quick way to get it to read database's boolean information, but I welcome other methods too!
I'm used to doing confirmations with buttons with async and window.confirm:
<button onClick={async hiStackOverflow => {
if (window.confirm("Want to do this?")) {
// saving to database here
}
}}>
But I didn't figure out how to do it with a checkbox. In short, I want for the page to confirm (and then save to database), when the user toggles on/off the checkbox.
// personData = database table, with boolean "recurring"
// row = which entity in a table we are talking about
function CheckboxThing ({ row, personData }) {
const checkbox = useCheckboxState({state: personData[row].recurring});
return (
<div className="checkbox-admin-other">
<Checkbox
{...checkbox}
// what here?? onClick or something?
/>
</div>
);
}
Reakit's checkbox can be used like this:
const toggle = () => setChecked(!checked);
return <Checkbox checked={checked} onChange={toggle} />;
This means that the checkbox will be checked if the variable 'checked', which needs to be put in the state of your React component, is true and that the method called 'toggle' will be called when the user toggles the checkbox. In that method, you can put the code which will show the Confirmation Prompt and then change checked if user clicked 'Yes' or leave it as it is if they check 'No'.
You can "observe" changes on checkbox.state using React Hooks:
function CheckboxThing({ row, personData }) {
const checkbox = useCheckboxState({ state: personData[row].recurring });
React.useEffect(() => {
// checking if state has changed
if (checkbox.state !== personData[row].recurring) {
if (window.confirm("Want to do this?")) {
// saving to database here
} else {
// revert checkbox state otherwise
checkbox.setState(!checkbox.state);
}
}
}, [checkbox.state, checkbox.setState, personData[row].recurring]);
return (
<div className="checkbox-admin-other">
<Checkbox {...checkbox} />
</div>
);
}
With React.useEffect, the user will see the checkbox checked before window.confirm opens. But you can use React.useLayoutEffect instead if you want it to open before checkbox state changes on the UI.
After coding around a little while, I found the solution! It turns out, you can put async inside Reakit Checkbox. Thanks to Tomislav and Diego, their answers helped me try different things and get it clean!
Here's the full function:
// admin can edit the right to join back to the queue after getting to the front
function RecurringBox ({ row, personData }) {
// sets the original values
const checkbox = useCheckboxState({state: personData[row - 1].recurring});
return (
<Checkbox {...checkbox} onChange={async checkboxSwitch => {
if (window.confirm("Change it?")) {
checkboxSwitch.persist();
// saving it to the database
await put(`${process.env.API_PATH}/person`,
{
"id": personData[row - 1].id,
"name": personData[row - 1].name,
"recurring": checkboxSwitch.target.checked
});
reload(`${process.env.API_PATH}/person`);
} else {
return null;
}
}}/>
);
}

How to provide auto-increment for every time onPress function called in React Native?

I am working on this React Native project for self-learning purposes. Every time I press the submit button, I would like to id value to auto-increase. For example, if the latest submitted item's id was 7, I want new ID to be 8. I thought if I can find the highest value in my ID row, then I can add 1 to it and make a new id for the new submitted item. I looked around and came up with a solution like this:
handleAddToList = () => {
let items = [...this.state.items];
let lastId = Math.max.apply(null, items.id);
let newId = lastId + 1;
items.filter(i => i.title == this.state.text).length === 1
? alert("This item is on the list already.")
: items.push({
id: newId,
title: this.state.text,
found: 0,
value: 0,
icon: "delete"
});
this.setState({ items: items });
};
And here is my little form that I submit the text:
<Input
placeholder="Start typing"
label={"Your next shop list item..."}
onChangeText={text => this.setState({ text })}
/>
<Button title="Add to list" onPress={() => this.handleAddToList()} />
My function actually adds the item for the first time, but when I add a new item, then I get this warning:
Warning: Encountered with two children with the same key, '"-infinity"'.
And this is how I list my items:
<ListItem
key={l.id}
title={l.title}
rightIcon={{ name: l.icon, color: "#cc0033" }}
bottomDivider={true}
/>
That means that lastId and newId parts of my code does not work properly.
I would appreciate any help on how I can make my ID's increase with every new submitted item.
it's about your lastId variable. which is fixable like this:
Math.max.apply(null, items.map(item => item.id))
You seem to have found the answer above already. In any case, if you wanted to avoid keeping track of your local IDs you can render your items is using the index as a key and avoid handling/creating unique IDs in the front end.
{
this.state.item.map((item, index) => (
<ListItem
key={index}
title={item.title}
rightIcon={{ name: item.icon, color: "#cc0033" }}
bottomDivider={true}
/>
)
}
Note: A reason why you would like to not create IDs locally, is because most times unique IDs already come from the backend.

Resources