Retrieve clicked array item on React Component - javascript

I have a Search component that outputs values from an array to a ResultItem child component, every child component has a button with a onClick property on it. I bound a function to the button to get the value of an array item that I clicked.
What I have working is that every time I clicked on every single ResultItem button I get values of 0,1,2,3,4,5,6 individually which is perfect but I dont need the array indexes I need the values of those indexes
class ResultItem extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick
}
handleClick(index) {
// index = this.props.applications[0]
// index = this.props.applications.map(obj => obj.videoId[0])
console.log('this click', index)
}
render() {
// console.log ('myProps', this.props.applications[0]);
const {applications} = this.props;
return (
<div>
{
applications.map((app, k) => {
return (
<Card key={k} style={styles.appCard}>
<CardMedia style={styles.appMedia}>
<div>
<Drafts color={white} style={styles.iconer}/>
</div>
</CardMedia>
<CardTitle key={k} title={app.videoId} subtitle="Application"/>
{/* <div key={k}><h3>{app}</h3></div> */}
<CardText>
<div>
<div>Status:
<b>test</b>
</div>
</div>
</CardText>
<FloatingActionButton
style={styles.addButton}
backgroundColor={'#CC0000'}
onClick={this.handleClick.bind(this, k)}
>
<ContentAdd/>
</FloatingActionButton>
</Card>
)
})
}
</div>
);
}
}
What I've tried so far:
if I use:
index = this.props.applications[0]
I get the first value of the array on ALL buttons I click on and
If I use:
index = this.props.applications.map(obj => obj.videoId[0])
I get the first letter of every single item of the array inside another array on every click, Is there any way I can get the value of the element I've clicked on , if so how?

When you map over an array you provide a function where the first argument is the current item, and the second one is the current index (of that item).
someArray.map((currentItem, currentIndex) => /* do stuff */ )
If you just care about the item, then there is no need to involve the index. You could just pass the current item directly to handleClick.
render() {
const {applications} = this.props;
return (
<div>
{
applications.map((item) => {
<FloatingActionButton
style={styles.addButton}
backgroundColor={'#CC0000'}
onClick={ this.handleClick.bind(this, item) }
>
</Card>
)
})
}
</div>
);
handleClick would then deal directly with the item, not the index. If you still want to use indexes, perhaps because that's nice to have if you need to manipulate the array at some later stage, then the index (second argument), let's call it index, could be passed to handleClick as before, and you would use that index to find the relevant item with
const clickedItem = this.props.applications[index]

index = this.props.applications[index]
or
index = this.props.applications[index].videoid

Related

Custom function I made that returns <span> elements is getting "Warning: Each child in a list should have a unique 'key' prop"

I am trying to create a function that takes an array of words such as:
const words = ['hello', 'my', 'name']
and returns them in the form:
<>
<span>hello</span> // <span>my</span> // <span>name</span>
</>
This is the react component I created to do this:
function StyleWords({words, ...props}){
return(
words.map((word, index, words) => {
if(index != words.length-1){
return <><span key={word} className='highlight'>{word}</span> // </>
}
else{
return <span key={word} className='highlight'>{word}</span>
}
})
)
}
and then call it like so:
<div><StyledWords words={['hello', 'my', 'name']} /></div>
Now this does work but I get the warning about keys. So my thinking is that either I have done this inappropriately or that I have missed something out with the keys. Any help?
You need to provide the key to the component which is the root of the item in the list.
function StyleWords({words, ...props}){
return(
words.map((word, index, words) => {
if(index != words.length-1){
return <React.Fragment key={word}>
<span className='highlight'>{word}</span> //
</React.Fragment>
}
else{
return <span key={word} className='highlight'>{word}</span>
}
})
)
}
As Lokesh suggested, you should use a unique value as key for the items instead of using word if it is not guaranteed that it will be unique.

Remove element from useState array by index

SOLUTION: Update the key value for the input element to refresh the default value => content of the input element. Deleting an element from the array DID work. Thanks for your help!
src: https://thewebdev.info/2022/05/12/how-to-fix-react-input-defaultvalue-doesnt-update-with-state-with-javascript/#:~:text=state%20with%20JavaScript%3F-,To%20fix%20React%20input%20defaultValue%20doesn't%20update%20with%20state,default%20value%20of%20the%20input.
I got an useState array in my code which represents a lisst of students:
const [students, setStudents] = useState([""]);
This array gets mapped to student elements:
{students.map((student, index) => <Student setStudents={setStudents} students={students} id={index} key={index} content={student} />)} I also got an AddStudent element which adds students to the array.
function AddStudent(props) {
const {setStudents} = props;
return (
<button className="change-student add-student" onClick={() => {
setStudents((students) => [...students, ""])
}}>
+
</button>
);
}
The RemoveStudent component is supposed to remove a student by its index in the array. I've tried many different ways but none worked correctly. How can I get it to work? Here is my code:
function RemoveStudent(props) {
const {students, setStudents, id} = props;
return (
<button className="change-student remove-student" onClick={() => {
let data = students;
if(id > -1) {
data.splice(id, 1);
}
console.log(data)
// setStudents(data)
// alternative:
// setStudents(students.filter(index => index !== id)); // removes the last element in the list
// doesn't work properly
}}>
-
</button>
)
}
Thanks for your help!
2 things should be noted here:
While updating react state arrays, use methods that return a new array (map, filter, slice, concat),
rather than ones that modify the existing array (splice, push, pop, sort).
While updating React state using its previous value, the callback argument should be used for the state setter. Otherwise you may get stale values. (See React docs).
if(id > -1) {
setStudents(students=> students.filter((s,i)=>(i != id)))
}
Consult this article, for a complete reference about how to update React state arrays.
You need to copy the students array first and then try removing the student by index. I assume by id you mean index at which to remove the student. Then you can try something like:
function RemoveStudent(props) {
const {students, setStudents, id} = props;
return (
<button
className="change-student remove-student"
onClick={() => {
if(id > -1) {
const data = [...students]; // making a copy
data.splice(id, 1); // removing at index id
console.log(data)
setStudents(data)
}
}}
>
-
</button>
)
}
With array.filter() you have a mistake in how you pass callback to filter() method. Please try the following:
setStudents(students.filter((,index) => index !== id));
Notice the index is second param of the callback so I used a , before index.
After #Irfanullah Jan 's answer you should make sure how you show the student.
Here is the simple example:
const [students, setStudents] = useState([1, 2, 3]);
return (
<div>
{students.map((student, index) => {
return <div>{student}</div>; // show the value not the index
})}
<button
onClick={() => {
let id = 1;
const copy = [...students];
copy.splice(id, 1)
console.log(copy)
setStudents(copy);
}}
>
-
</button>
</div>
);
The code above will delete the student of "index==1"

filtering out an array of items setting state

I am looking to filter through an array and return all elements of the array except the element which has been clicked on, so I have a map of list elements each with a key={index} of their map, onClick it should call my remove function, and pass in the index of the element to be removed, I then need to filter over that array, remove the element, update state, and send this information to my backend.
here is the delete function
const deleteItem = (id) => {
// use filter, to loop through all pieces of index
const element = list.todoItems.indexOf(id - 1);
setList({ todoItems: list.todoItems.filter(element !== id) });
console.log(list.todoItems);
dispatch(updateTodo(list));
};
here is the mapped array
{list.todoItems.map((Item, index) => (
<div
// setup anonymous function, that will call
// ONLY when the div
// is clicked on.
key={index}
onClick={() => deleteItem(index)}
>
{/* list item, gets text from props */}
<li>{Item}</li>
</div>
))}
I must be missing something, because this should work, Though i may have to shift gears and have each item as an actual object in my database, though id rather not do this as i feel an array of strings is more than appropriate for this app.
Remove your indexOf logic and this will work.
You don't have to find the index of the array because you're already receiving it as a parameter.
const deleteItem = (id) => {
setList({ todoItems: list.todoItems.filter((_, filterID) => filterID !== id) });
console.log(list.todoItems);
dispatch(updateTodo(list));
};
You don't need to subtract one from the index on the indexOf function
And for this case, splice works better than filter
const deleteItem = (id) => {
const element = list.todoItems.indexOf(id);
setList({ todoItems: {...list}.todoItems.splice(element, 1)});
dispatch(updateTodo(list));
};

Cannot map value of object seen in console

My idea is to click on any of the buttons on the left navbar and whenever the name of the clicked button in the logos object matches any of the names in the items object within projects, then display those objects.
When I click on any of the buttons on the left, I transform that object's active property to true within the logos object. After I've filtered the values, I can see all of the correct values in the console, but I can't loop through them - with a for loop or a map. Oddly enough, when I write filteredValues[0] I am able to output that data to the screen but since I want multiple outputs for some of these clicked values, this is not an option. Any help is appreciated. Thank you!
These are the items that I can't loop through but am getting back when I console log them
These are my projects
These are my logos
const Homepage = () => {
const {state} = useContext(Context)
const {projects,logos} = state;
return (
<>
<div className="container">
<Language />
<div className="homepage">
{logos.map(logo => {
let filteredValues;
if(logo.active == true){
filteredValues = Object.values(projects).filter(({items}) => Object.values(items).includes(logo.name))
filteredValues.map((val) =>{
console.log(val)
return(
<div>{val.title}</div>
)
}) //end of filteredValues.map
} //end of if
}) // end of logos.map
}
</div>
</div>
</>
)}
An Array.map() would always return an Array of same length, irrespective of you return anything or not. If you do not return anything then
it would be a sparse Array with empty values.
Try returning only the array with the required data. Here I have just separated out the Logos logic in a separate variable.
render() {
const Logos = (
<div className="homepage">
logos.reduce((acc, logo) => {
if (logo.active) {
const filteredValues = Object.values(projects).filter(({items}) => Object.values(items).includes(logo.name));
Object.values(projects).forEach(({items}) => {
if (Object.values(items).includes(logo.name)) {
acc.push((<div>{val.title}</div>));
}
});
}
return acc
}, [])
</div>
)
return (
<div className="container">
<Language />
{Logos}
// rest of the code
)
}

React incrementing variable within .map() function

I am mapping through an array, and I want my variable i to be used as a unique key for my Components, however I do not know how (or where) to increment it correctly, if I add a {i++} within the <Component> tags then it will display the value of i on screen, and if I instead add {this.function(i)} and place the i++ inside the function, it will call the function but the variable i will reinitiate to the value of 0 everytime, so the key value will not be unique. I need the value of i to be the key for the component and it has to be incremented by 1 everytime, does anyone know how I can achieve this? Also, as you can see in the code, when the component is clicked it will make a function call which will send the value of i of the clicked component as a parameter to the called function.
Code:
function(i) {
console.log(i)
}
render() {
var i = 0;
var {array} = this.state;
return (
<div className="App">
{array.map(item => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
))}
</div>
);
}
The map function gets a second parameter which is the index of the element:
{array.map((item, i) => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
)}
Be aware that if you intend to sort this array or change its contents at runtime, then using array index as a key can cause some mistakes, as sometimes an old component will be mistake for a new one. If it's just a static array though, then using index as a key shouldn't be a problem.
.map already offer the increment, just add a second variable to the callback
render() {
var {array} = this.state;
return (
<div className="App">
{array.map((item,i) => (
<Component key={i} onClick={(e) => this.function(i, e)}>
<p>{item.name}</p>
</Component>
))}
</div>
);
}
You could try array.map((x, Key) => console.log(key)); ..
In place of console.log you could add your code, it should work fine as per your requirement.

Categories

Resources