React change selected item index of array using order number - javascript

Hi I am new developer at Reactjs. I have a question. It's maybe simple but I want to learn best practice or best way about changing selected item index of array according to user order number. I mean user will give a number from Input and I will use that number for placement inside the array.
Example Object Array in My State :
const [state, setState] = useState({
dragFrames:
[{id:1,name:"test-1",zIndex:1},
{id:2,name:"test-2",zIndex:2},
{id:3,name:"test-3",zIndex:3}]
})
User zIndex Input "1" to item which id is 3
My expected new Array order is below:
dragFrames:
[id:3,name:"test-3",zIndex:1},
{id:1,name:"test-1",zIndex:2},
{id:2,name:"test-2",zIndex:3}
]
I can change and set zIndex number of any object but I cant change array order according to input value. How can I do this? Could you help me ?
I tried this before and I faced endless loop:
if (e.target.value>0) {
nowDev.z = e.target.value
var arrayCopy = state.dragFrames.slice()
state.dragFrames.map((item:any,index:any)=>{
if (index===e.target.value) {
arrayCopy.push(nowDev)
}else{
arrayCopy.push(item)
}
})
setState({ dragFrames: arrayCopy})
}

This is sorting example. You need to push elements first.
if (e.target.value > 0) {
const newArray = state.dragFrames.map((el, index) => index === e.target.value ? e.target.value : item);
setState({
dragFrames: newArray.sort((a, b) => a.zIndex.localeCompare(b.zIndex))
})
}

First you need to change zIndex of all elements in the array.
Algorithm for it will be following:
Map over the array and increment zIndex of all elements
for the element which is set by user, set it's zIndex to 1
Once above is done, sort by zIndex.
Code is mentioned below:
df = [{id:1,name:"test-1",zIndex:1},
{id:2,name:"test-2",zIndex:2},
{id:3,name:"test-3",zIndex:3}];
const updatedDF = df.map(x => {
if(x.id === 3) return {...x, zIndex: 1} // replace 3 with e.target.value
else return {...x, zIndex: x.zIndex + 1}
});
updatedDF.sort((x, y) => x.zIndex - y.zIndex)
console.log(updatedDF)

Related

React Native: remove element from array if id is already present

in my react native app I have a list of tags where people can choose from, they click on an item and add it to the list of array of item ids, if they click over an item which id is already in the array, I want to remove it from array.
Right now all I can do is add ids to array, I can't remove it if already present.
PD: I also check if list of ids is lower than 10. Also, is there a cleanest way to write the function?
const [selectedItems, setSelectedItems] = useState([]);
const toggleItem = useCallback((itemId) =>
{
setSelectedItems(prev => prev.includes(itemId) ? prev.filter(obj => obj.id != itemId) : prev.length < 10 ? [ ...prev, itemId] : prev);
},[])
It looks like you're mixing and matching the items in the array - is it a list of IDs, or the list of objects?
prev.includes(itemId) // This looks like a list of IDs
? prev.filter(obj => obj.id != itemId) // This looks like a list of objects
In terms of "a cleaner way to write the function" - you can be a bit more efficient if you don't do the includes check first, as that involves an extra iteration over your items. In the example below I've changed the callback function to assume it gets passed the whole item, instead of just the id:
const [selectedItems, setSelectedItems] = useState([]);
const toggleItem = item => setSelectedItems(prev => {
const next = prev.filter(selectedItem => selectedItem.id !== item.id);
// The item wasn't removed from the list so it needs to be added
if (next.length === prev.length && next.length < 10) {
next.push(item);
}
return next;
});
You could also consider using a Map instead of an array:
const [selectedItems, setSelectedItems] = useState(new Map());
const toggleItem = item => setSelectedItems(prev => {
const next = new Map(prev);
if (!next.delete(item.id) && next.size < 10) {
next.set(item.id, item);
}
});
// If you need the items as an array
const selectedItemsArray = Array.from(selectedItems.values());
Finally - I'd also remove the useCallback because you probably don't need it.

How to delete an element from array in react?

I have two functions , one of them adds an item in array and the other one delete from that array using React JS (hooks).[Both are handler of click event].
What I have works incorrectly.
``id`` comes from ``contact.length`` and I deleted it with``contacts.splice(id, 1)``.
I dont have any idea why it has this problem.
it doesnt delete what would be clicked but a random one.
function handleAddRecord(nameValue, phoneValue) {
setContacts([...contacts , {
id : contacts.length,
name : nameValue,
phone : phoneValue
}])
}
function handleDelete(id) {
console.log("manager", id);
const newContacts = contacts.splice([id],1);
setContacts([...newContacts]);
}
One of the issue on the implementation is id generation keeping it array length could lead to issue as you delete and add elements there could be scenarios where there is same id for multiple items.
One of most widely used generator is uuid https://www.npmjs.com/package/uuid
Usage
const uuid = require("uuid");
uuid.v4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
Now use this in your implementation
Add Operation:
const handleAddRecord = (nameValue, phoneValue) => {
const newRecord = {
id: uuid.v4(), // This should be unique at all times
name: nameValue,
phone: phoneValue,
};
setContacts([...contacts, newRecord]);
};
Delete Operation:
Use filter rather than splice as for splice you'll need to find the index of the element with id. But with Filter it can be done is a single line
const handleDelete = (id) => {
setContacts(contacts.filter(item => item.id !== id));
};
Here we're assuming that id is the index of the element to be removed.
The splice function returns the removed elements, thus is not useful to take its result. Instead, make a copy of the array first, then remove the undesired element:
function handleDelete(id) {
console.log("manager", id);
const newContacts = [...contacts];
newContacts.splice(id,1);
setContacts(newContacts);
}
That's because splice alters the array itself.
More here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
Ok, id return index of current map?
Follow this example:
const assoc = [...contacts];
assoc.splice(id, 1);
setContacts(assoc);
You can delete the item by finding its index from array.
For Example:
function handleDelete(id) {
console.log("manager", id);
const index = contacts.findIndex((x) => x.id === id);
const newContacts = [
...contacts.splice(0, index),
...contacts.splice(index + 1),
];
setContacts(newContacts);
}
You need undestand, every time when i'll remove a item from a array of a index, that this index has use unique key... When React remove a item 6 (a example) this is remove of array first, and when react re-render function react can delete another component, because a array exist key 6, wehn you have more 6 item from array... Understand?
Follow a example:
import React, { useState } from 'react';
function User(data) { // data is a array
const [contacts, setContacts] = useState(data); // data return a list of contacts
/* contacts result a array object and has the following attributes
[{
name: 'Cael',
tel_number: '+55 11 9999-999',
e_mail: 'user#example.com',
! moment: "2021-06-15T05:09:42.475Z" // see this a date in ISO string
}]
*/
// about moment atribute:
// this atribute result when use `new Date().toISOString()`
// and this value is added in the moment that create a object in array list
// It's mean that every time result a unique key
const deleteFn = (val) => { // val result index of component and item array
const assoc = [...contacts];
assoc.splice(val, 1);
setContacts(assoc);
}
return (
<div>
{!!contacts.length &&
contacts.map((assoc, i) => { // variable i result in your id
const { moment, name, e_mail, tel_number } = assoc; // moment use a unique key
return (
<li key={moment}>
<span>{name}</span>
<span>{e_mail}</span>
<span>{tel_number}</span>
<button type="button" onClick={() => deleteFn(i)}>Delete</button>
</li>
);
})}
</div>
);
}
export default User;
I hope, this helpfull you!

How to dynamically handle a multitude of filters with react?

I am currently working on an online store that filters products based on certain criteria such as size, stock, gender, etc.
While I have been able to make it work to a certain extent. My program currently filters by size, gender, sorts by price etc. However, I cannot get it to filter by brand. For some reason once I click on the brand, I am able to filter the function once, however, once I click on another brand the filter for that particular brand does not run.
Here is the link to the code sandbox:
https://codesandbox.io/s/mystifying-roentgen-7mp0t
I am currently stuck with filtering by brand and I have tried to compared my filtered result to the state of the item clicked, by checking if the brand is included in the item and by using localeCompare().
Here is the link to the code sandbox:
https://codesandbox.io/s/mystifying-roentgen-7mp0t
createCheckboxes = () => available_sizes.map(this.createCheckbox);
handleFormSubmit = event => {
//4) this button updates the filters on the sizes, which I think I need to fix to update the brands, the price and the gender
event.preventDefault();
//5) right here I am storing the selected checkboxes which is what I was doing before by pushing the checkboxes
const selectedSizes = [...this.selectedCheckboxes];
const shallowCopy = [...this.state.filteredProducts];
let filteredProducts = shallowCopy.filter(product =>
selectedSizes.every(size =>
product.stock.some(s => s.stock > 0 && s.size === size)
)
);
let filteredGender = filteredProducts.filter(product => {
return product.gender.some((item, idx, arr) => {
return item[this.selectedGender] === false ? null : product;
});
});
//***this is the function that is not currently running***//
let filteredData = filteredGender.filter(product => {
//console.log(product.brand.includes(this.state.activeBrand))
//console.log(product.brand = this.state.brand)
return product.brand.includes(this.state.activeBrand)
});
let sortedPrice = filteredData.sort((a, b) => {
return this.state.sortBy === "min"
? a.price - b.price
: b.price - a.price;
});
this.setState({
filteredProducts: sortedPrice
});
};
I am expecting to be able to filter by brand wit this function, once an item is clicked.
Here is the link to the code sandbox:
https://codesandbox.io/s/mystifying-roentgen-7mp0t
There are 2 errors in your application:
1) the first one is reported by #user753642 in comment to your question, remove this line from index.js, because it sets your brand of all products to "":
console.log(product.brand = this.state.brand)
2) you are filtering filteredProducts and no the all products. While after first filtering on brand the filterdProducts does not have any item of other brands, it returns an empty collection after filtering on another brand. Change line in handleFormSubmit in index.js, from:
const shallowCopy = [...this.state.filteredProducts];
to:
const shallowCopy = [...this.state.products];

Reordering index when mapping arrays with React

I'm changing object property inside mapping and I want to change index when object property is changed ( = input disabled) , so whats best way to do it?
I've tried making new array for index but can't make it work because then it would need some nested mapping with separate arrays and cant make it work.
edit: I use index to mark text part position in official document, thats why this index is so important.
onToggleTextPart = (e, index) => {
const node = this.myCheckbox[index]
let newStates = [ ...this.state.meeting_parts ];
if(node.checked) {
newStates[index - 1].type_tag = "Included";
}
else {
newStates[index - 1].type_tag = "notIncluded";
newStates.splice(index-1, 1)
}
this.setState({ meeting_parts: newStates });
}
return _.map(meeting_parts, (meet, index) => {
let checked = meet.type_tag === "Included" ? true : false;
return
<div className="form-group">
<div className="input-group">
<div className="input-group-prepend">
<span className="input-group-text minutes-agenda-item-number">
{index} (THIS is the index i want to change)
</span>
</div>
I want to i.e when i hide one object from Index 6, it "gives away" its index and Index 7 takes its position.
Okay as far as I understand you want to do this:
meeting_parts.filter((meet)=>meet.type_tag === "Included").map((meet, index)=>{
// your mapping function here
});
Filter will return an array of the meetings which type_tag is "Included".
You can read about the filter function.
EDIT:
let includedCount = 0;
meeting_parts.map((meet, index)=>{
if(meet.type_tag === "Included") {
includedCount += 1;
}
// your mapping function here but use inlcudedCount instead of index
});
Of course now it displays some numbers mutpliple times. If you don't want them displayed you have to add logic to disable the rendering when necessary.

Is it possible to map only a portion of an array? (Array.map())

I am building a project using React.js as a front-end framework. On one particular page I am displaying a full data set to the user. I have an Array which contains this full data set. It is an array of JSON objects. In terms of presenting this data to the user, I currently have it displaying the whole data set by returning each item of data using Array.map().
This is a step in the right direction, but now I need to display only a portion of the data-set, not the whole thing, I also want some control in terms of knowing how much of the total data set has been displayed, and how much of the data set is yet to be displayed. Basically I am building something like a "view more" button that loads more items of data to the user.
Here is what I am using now where 'feed' represents my Array of JSON objects. (this displays the whole data set.)
return (
<div className={feedClass}>
{
feed.map((item, index) => {
return <FeedItem key={index} data={item}/>
})
}
</div>
);
I am wondering if it is possible to use .map() on only a portion of the array without having to break up the array before hand? I know that a possible solution would be to hold the full data set, and break it off into portions, and then .map() those portions, but is there a way to .map() a portion of the array without having to break it up?
Any and all feedback is appreciated. Thanks!
Do not try to solve this problem with a hack in your mapping step.
Instead, slice() the list to the right length first before the mapping:
class Feed extends React.Component {
constructor(props) {
super(props)
this.handleShowMore = this.handleShowMore.bind(this)
this.state = {
items: ['Item A', 'Item B', 'Item C', 'Item D'],
showItems: 2
}
}
handleShowMore() {
this.setState({
showItems:
this.state.showItems >= this.state.items.length ?
this.state.showItems : this.state.showItems + 1
})
}
render() {
const items = this.state.items.slice(0, this.state.showItems).map(
(item) => <div>{item}</div>
)
return (
<div>
{items}
<button onClick={this.handleShowMore}>
Show more!
</button>
</div>
)
}
}
ReactDOM.render(
<Feed />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'></div>
The easiest way in my head is just to use a filter and map
const feed = [1,2,3,4,5,6,7,8,9,10,11]
feed.filter((item, index) => index < 5).map((filteredItem) => //do somthing with filtred item here//)
where 5 is just a number of items you want to get
you could use the slice function before to map the array, it looks like you want to do some pagination there.
var fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
var citrus = fruits.slice(1, 3);
// fruits contains ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango']
// citrus contains ['Orange','Lemon']
Array.reduce should do what you're asking for. Just change the if statement depending on which range you want.
var excludeAfterIndex = 5;
feed.reduce((mappedArray, item, index) => {
if (index > excludeAfterIndex) { // Whatever range condition you want
mappedArray.push(<FeedItem key={index} data={item}/>);
}
return mappedArray;
}, []);
If you just want to map a portion of an array, you should first filter() your array to obtain the expected portion according to conditions :
array.filter(item => <condition>).map();
Yes, you can map portion of array, based on index. For example:
yourArray = yourArray.map(function (element, index, array) {
if (array.indexOf(element) < yourIndex) {
return {
//logic here
};
} else {
return {
//logic here
};
}
});
You can use slice to get portion of an array:
const data = [1,2,3,4,5,6,7,8,9,10,11]
var updatedData = data.slice(0, 3);
Array#map iterates over all items.
The map() method creates a new array with the results of calling a provided function on every element in this array.
You could use Array#filter
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
for the wanted items and then apply map for the wanted format.
There is no version of the map() function that only maps a partial of the array.
You could use .map() in conjunction with .filter().
You get the index of the current element as the second arg of map and if you have a variable for current page and page size you can quite easily filter the right page from your array without having to really slice it up.
var currentPage = 1;
var pageSize = 25;
dataArray.filter(function(elt, index) {
var upperThreshold = currentPage * pageSize;
var lowerThreshold = currentPage * pageSize - pageSize;
return index < upperThreshold && index > lowerThreshold;
});
Using slice() is better than adding a condition to your map or reduce function, but it still creates an additional, unused copy of that segment of the array. Depending on what you're doing, that might not be desired. Instead, just use a custom map function:
function sliceMap(fn, from, toExclusive, array) {
const len = toExclusive - from;
const mapped = Array(len);
for (let i = 0; i < len; i++) {
mapped[i] = fn(array[i + from], i);
}
return mapped;
};
Note that fn receives the array value and the (now) zero-based index. You might want to pass the original index (i + from). You might also want to pass the full array as a third parameter, which is what Array.map does.
Use this, easy approach
const [limit, setLimit] = useState(false);
const data = [{name: "john}, {name: 'Anna'}]
Here we will have 2 cases:
Display only first data which is John
Display all
data.slice(0, extended ? data.length : 1).map((item, index) => <Text>{item.name}</Text>)
....

Categories