React Fontawesome Icon on Button not Allowing Id to be Accessed - javascript

I am using react fontawesome in my project. Under the render function of my component I have the following which maps a school onto the page and then I want a button with an edit icon to perform an action. My code for the button looks as below:
{ this.state.myschools.map( (school) => (
<Row key={school.id}>
<Col><p>{school.name}</p></Col>
{ school.deaths.length > 0 ? <Col md="2"><button id={school.id} className="likelink" onClick={this.goToSchoolDeathList}>View Deaths</button></Col> : <Col md="2"></Col> }
<Col><button id={school.id} onClick={this.editSchool}><FontAwesomeIcon icon={faEdit} /></button></Col>
</Row>
))}
The editSchool function is as:
editSchool(e) {
console.log("School Id");
console.log(e.target.id);
}
If I click on the empty space around the font awesome icon then the id is logged to the console. If I only click on the area where the icon exits then the id is not logged. I want the user to be able to click on any part of the button including the fa icon and to capture the school id.
I have tried adding the onClick event and the id attribute to the "FontAwesomeIcon" component, but that still doesn't work.
Can anyone help?

Well, this is one of the common pitfall you learn during your time with React. Rest assure, we have all fallen into this pit.
There are few AHA moments here.
1st Aha: React doesn't have a usual way to let you get element attribute in an event (an e in your example).
That being said, you can still access to the button element by e.target and use JavaScript to get attribute.
Solution #1 JavaScript getAttribute
editSchool(e) {
console.log("School Id");
console.log(e.target.getAttribute('id'));
}
e.target will be the HTML Element that you click, and we can use JavaScript way to get attribute. (Please Note that, Javascript way is not React way)
But there are another way and you'll see this way more often.
Solution #2 You can define a function directly in a render
<button id={school.id} onClick={e => this.editSchool(school.id)}>
Please be careful about this another pitfall,
2nd Aha: Bind onClick to function, not result of a function.
Don't do this
<button id={school.id} onClick={this.editSchool(scholl.id)}> // This is incorrect
The thing is onClick prop expect a function to call when someone click.
The first one we define a function e => this.editSchool(school.id) and bind onClick with the newly defined function.
While the second, we just bind onClick to a "result" of function this.editSchool(school.id), which is not a function.

Related

Why does the return value change when using React-icon component for my button?

I want to delete a list, and style that button with an icon. If I just have "delete" as my button, my onClick returns the ID as expected. However, when I try to use a component for my button, it returns a weird object.
I tried using different elements instead of a <Button/> and different icon libraries, but it results in the same behavior. This is how I was importing the component and I am using styled-components if that matters.
import React from "react";
import { Draggable } from "react-beautiful-dnd";
import {BsXCircle} from "react-icons/bs"
import styled from 'styled-components'
Below is my code for my component that renders the list's title, and a button that would delete the list:
<div
{...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
<div value={props.list._id} onClick = {props.handleListSelect} className="list">
<button value={props.list._id} onClick={props.handleDeleteList} >
<BsXCircle/>
</button>
{props.list.title}
</div>
</div>
The code snipper below works as expected.
<button value={props.list._id} onClick={props.handleDeleteList} >
delete
</button>
My props.handleDeleteList function is below:
const deleteList = async (e) =>{
console.log(e.target.value)
}
If I use <BsXCircle/> , my console log is this:
{stringValue: '"undefined"', valueType: 'string', kind: 'ObjectId', value: 'undefined', path: '_id', …}
I am confused of why this is happening.
Isn't my value that I am passing assigned in the value attribute of my button? Why does rendering a component instead of a text change the value of my button?
I have simplified my code just for asking this question.
First thing, the <button/> element should accept only certain types of nested elements: MDN ref
Permitted content Phrasing content but there must be no Interactive content
That said, your problem is that nested elements of <button/> inherit its event listeners, so when you nest the react-icon Component ( an <svg> I guess ), and click on the button, you basically are dispatching the click event on the svg and not on the <button/>. You can address the issue with a simple css fix:
button > * {
pointer-events: none;
}
This way you are disabling all event listeners on button's children and it should fix it.

How to send data from v-for to event handler in Vue?

I am having problems with sending the right data to the event handler of a subcomponent in my Vue component.
Here is my code:
<template>
<div>
<div v-for="item in [1, 2, 3]">
<div #click=test(item)>test</div>
<ConfirmModal v-if="showModal" #confirmed=test(item) />
<button v-on:click="showModal = true" ></button>
</div>
</div>
</template>
<script>
import ConfirmModal from 'ConfirmModal'
export default
{
components: {ConfirmModal},
data()
{
return {showModal: false}
},
methods:
{
test(item)
{
console.log(item);
this.showModal = false;
},
},
}
</script>
And here is the code for a subcomponent:
<template><div class="modal" #click.self="$emit('confirmed')">subtest</div></template>
So, basically, I here have an array of numbers and am using the v-for to iterate over an array.
For each item one test label, one subcomponent (containing a subtest label) and one button is created. When button is clicked, subcomponent is shown. When I click on the subcomponent (subtest label) it disappears.
div is a standard component which emmits #click events when it is clicked upon.
confirmModal is a very simple component which listens for #click events and redirects them into #confirmed events.
So, this code generates 3 times 3 items. When I click on any of the labels, test method is called which logs the value of the item.
When I click on any of the test labels, I get the correct value in the console, that is, 1 for the first item, 2 for the second item and so on.
But, when I click on any of the subtest labels, I allways get the 3, whichever link I clicked. (If I change the order of items in the array, then I allways get whichever item I put on the last place in the array).
I do not understand what is going on here, it is the same method called on the same way. Why is 3 allways logged in console when I click on the subtest label?
If you wonder what is class=modal in my subcomponent used for, I have no idea. This is a skeleton of two components in a very complex project consisting of thousands of files built on top of the Liferay, and there are more then ten files where is .modal defined in many directories. So I am not sure from which file does this particular component load this class. I did not throw it out of this skeleton code just because when I remove this class from the div in the subcomponent, everything works fine. I have no idea why. How can a class stop the correct data from being received? I do not get this.
I know that this bug may not be reproducable, because I did not provide the definition of the .modal style, but I posted it anyway, because I hope that you can see something else in the code what is wrong or propose another way how to execute the test method on #confirmed event.
I am using Vue 2.

ReactJS - update component (generated from a list) in the same div

It's a basic question but I searched for a guide without success...
I want to have a list of dropdowns and inputs and each dropdown will change the input next to it.
var list = [{ name: "foo1"}, {name: "foo2"}];
return (
{list.map( (name) => {
return (<div>
<dropdown data={someData}
onChange={(ev) => {
if(ev.value == 'clearIt')
<CHANGE THE NEAR INPUT VALUE>
}
}/>
<input value={name.name} />
</div>)
})});
I don't want to use DOM nor ref cause I understood that it's better to avoid it.
Any suggestions?
Or maybe the ref is the only option?
Thanks
So you can achieve this by doing the following steps:
Create a new component and move the dropdown and input to this new component.
Add state to your component by following this example:
https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
Add an event listener onChange to the dropdown with an event handler which can update the state you created in the first step. (Remember to bind the handler in the constructor.
Add the new component within the div element of this example you gave and pass the relevant data you need to the new component you created.
This should allow you to update only the input next to the dropdown. Also it allows you to have different data for each dropdown you created.

React and navigating to a selected item

I have a table of user I am displaying to a user. They click a row, and I want to navigate to a component to show the details.
<tbody>
{this.state.clients.map(client => (
<tr className="tableRow" onClick={() => this.handleSelection(client.id)}>
<td>{client.name}</td>
My Route is setup like this:
<Route path="/client/:clientId" component={Client} />
So in the row, I have an onClick event, which calls a function, passing the parameter selected. Here's the function:
handleSelection(clientId) {
this.props.history.push(`/client/${clientId}`);
}
Then, in the constructor of the target component, I am just logging the selected id, which will be passed to the api to load the data:
console.log('Loading clientId...', this.props.match.params.clientId);
But this seem wrong. I get the right ID and all, but .. am I doing this right? Is there a better/safer way to do what I am doing?
Another option would just be to use individual Link tags inside each TD:
<td><Link to={`/client/${client.id}`}>{client.name}</Link></td>
That would also be more accessible, since vision impaired users might have trouble triggering that onclick event.
If you're using react-router-dom it has a Link component
{this.state.clients.map(client => (
<Link to={`/client/${clientId}`} >
<tr className="tableRow">
<td>{client.name}</td>
</Link>
It's better to use a link rather than an onClick on on a table row for accessibility.
If you're not using react-router-dom you can roll your own version from looking at the source - https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/Link.js
By working in this way it'll behave correctly when people command click on the link to open it in a new tab.
For the routes you grab the params as you have, if you don't want the component to deal with match in the properties you can do something like this:
<Route path="/client/:clientId" children={({ match }) => (<Client {...match.params} />)} />

Passing a value into a method that is triggered by an event

I made a simple todo app using React.
An unordered list (<ul> is used to present all the items in my list. Within that are list items (<li>), and with a list item is a button with an onClick handle attached to a class method:
<button onClick={this.onHandleClick.bind(this)}>Add</button>
In the method, I need to prevent the default behaviour to prevent the page refreshing when the button is clicked:
onHandleClick(event) {
event.preventDefault();
...
}
My question is, what can I do if I want to pass a value into onHandleClick, given that event is given as the argument?
Wrap your event handler in another function and pass item specific parameters this way:
<button onClick={e => this.handleClick(e, itemId)}>Add</button>
handleClick(event, itemId) {
event.preventDefault();
console.log(itemId);
}
I'm not familiar with React, but if the value is static, you could do something like this:
<button onClick={function(){self.onHandleClick(passedInValue);}}>Add</button>
Note that "self" would need to be bound to whatever object contains the 'onHandleClick' method.

Categories

Resources