Getting values from material-ui Button in React - javascript

I have this function
handleChangeButton = (e) => {
alert(e.target.value)
this.props.setFieldValue('degreeLevel', e.target.value);
}
and in my component render, I have
<div className="twelve columns">
<p>Degree Level</p>
<Button
variant="raised"
label="Default"
onClick = { this.handleChangeButton }
value="Doctorate"
>
Doctorate
</Button>
<Button variant="raised" label="Default">
Masters
</Button>
<Button variant="raised" label="Default">
Undergraduate
</Button>
</div>
So, what I want to do is, when I click the Doctorate button, it should this.props.setFieldValue to degreeLevel which is one of the fields in my Formik form. When I click the button, the alert gives me undefined which means it's not reading the value Doctorate.
How can I make e.target.value read the value of the button?

Use currentTarget instead of target
handleChangeButton = (e) => {
alert(e.currentTarget.value)
this.props.setFieldValue('degreeLevel', e.currentTarget.value);
}

#yugantarkumar #monsto, This is not something that is specific to Material UI, rather this is how it is done in JS by using event bubbling. You can read more about the difference here: http://www.qc4blog.com/?p=650
currentTarget refers to the element, to which the event listener is attached to, while target indicates the element that was interacted with.
In MUI this issue is caused by clicking on MuiButton-label component instead of the MuiButtonBase component.

Related

useRef current and click event target are not equal even though they should be

I have a div element that I want to call a function from when a click event fires on it. The div contains an input field that I don't want to trigger the function, so I'm using useRef to target only the div element itself and not the contents. I'm creating the reference with the hook, giving the div a ref attribute pointing to that reference, and then passing it an onClick event that first checks if ref.current is equal to the event target. This method has worked for me in the past when building features such as modals, but for some reason this time the value of ref.current and the value of the event target are not evaluating as equal, and so the function never fires off. Here's the code:
const VideoEntryTile: React.FC = () => {
const [isExpanded, setIsExpanded] = useState<boolean>(false);
const containerRef = useRef<HTMLDivElement | null>(null)
const handleExpand = (e: any):void => {
console.log(containerRef === e.target) // This evaluates to false
if (containerRef.current === e.target) {
setIsExpanded(true)
}
}
return (
<div
className={styles.VideoEntryTile__Container}
onClick={handleExpand}
ref={containerRef} {/* The reference is set here */}
>
<div className={styles.VideoEntryTile__Top}>
<div className={styles.VideoEntryTile__LeftActions}>
<div className={styles.VideoEntryTile__LeftActions__VideoNameInput}>
<TextField
disabled={false}
inputSize="large"
value="Video 1"
/>
</div>
<div className={styles.VideoEntryTile__LeftActions__GuestsContainer}>
<p>Guests</p>
<input />
</div>
<button type="button">-</button>
<button type="button">+</button>
</div>
<div className={styles.VideoEntryTile__RightActions}>
<p>20</p>
<p>Credits</p>
</div>
</div>
{ isExpanded ?
(
<div className={styles.VideoEntryTile__Bottom}>
<p>Test</p>
<p>Test</p>
<p>Test</p>
<p>Test</p>
<p>Test</p>
</div>
)
: null }
</div>
);
};
Any idea why this isn't working? This solution has worked for me in the past so I'm not sure why it isn't now.
E: This thread should not have been closed - the provided "answer" has nothing to do with my question.
I know this is an old topic and the question is very old, but maybe it will be useful to someone. 🤷‍♂️
Code:
const handleExpand = (e: any):void => {
if (containerRef.current.contains(e.target)) {
setIsExpanded(true)
}
}
Problem:
In your case useRef is returning the current as an Object. By comparing the e.target which is a reference to the object onto which the event was dispatched and an Object the comparison will never be equal.
Solution:
Check if the reference to the object (returned by e.target) is contained inside the useRef.current node.

React Hook (useState, setState) is both true and false

So I have a React state variable const [pickingHotspot, setPickingHotspot] = useState(false);. I then have this button <button type="button" className="btn btn-outline-danger" onClick={() => setPickingHotspot(true)}> which just sets the state to true onClick. I have another handler
tmp.on('mousedown', (event) => {
if (pickingHotspot){
console.log(tmp.mouseEventToCoords(event));
} else {
console.log(pickingHotspot);
}
});
where tmp is a Pannellum 360 Image Viewer (its a third party library, but I don't think it matters what it is), and this is set in my useState(...,[]) which runs once on load. Lastly, I have an onClick div that just prints the value of pickingHotspot for debugging purposed. Here's the weird part:
When I load the page and click the debug div, the value is false. Cool, that works. Then I click the button (which should set it to true!) and then click the debug div again. The value is true! But when I click the Pannellum viewer, the value is false? I'm not sure how the value could possibly be both true and false, depending on where I click. Are there different versions/instances of these variables? I've tried linking everything to individual function handlers that are outside of the html components and outside of the useEffect in case there's some weird scope stuff happening, but nothing has worked so far.
I tried to show all of the code needed, but here's the full thing (I took most of the unrelated stuff out to simplify it, its a lot of code to look through.):
function TourCreator(props){
const [scenes, setScenes] = useState({});
const [media, setMedia] = useState({});
const [viewer, setViewer] = useState(null);
// Editor states
const [pickingHotspot, setPickingHotspot] = useState(false);
function handle(event){
if (pickingHotspot){
console.log(viewer.mouseEventToCoords(event));
} else {
console.log(pickingHotspot);
}
}
// Called once on load
useEffect(() => {
if (Object.keys(media).length == 0){
// Sends the request to the backend for "data"
sendGetRequest(window.$PROJECT, true, {
id: params["project_id"],
}).then((data) => {
data.images = reshapeArray(data.images, 3);
console.log(data)
setMedia(data);
let tmp = window.pannellum.viewer('panorama', tour)
setViewer(tmp);
// Print Pitch/Yaw on click
tmp.on('mousedown', (event) => handle(event));
});
}
}, [])
return (
<div className="p-3">
{/* MAIN CONTENT */}
<div className='tour-creator-root mx-auto p-3 row rounded'>
{/* MAIN BOX */}
<div className='main-box col-9 px-0 rounded'>
{/* Pannellum viewer */}
<div id='panorama' className="w-100 rounded-top">
<button type="button" className="save-button btn btn-outline-danger">
Save
</button>
</div>
{/* Toolbar */}
<div className="toolbar w-100 d-flex flex-column justify-content-center rounded-bottom p-3"
onClick={
() => {
console.log(pickingHotspot)
}
}>
<div className="d-flex flex-row justify-content-around">
<button type="button" className="btn btn-outline-danger" onClick={
() => setPickingHotspot(true)
}>
Add Hotspot
</button>
</div>
</div>
</div>
</div>
</div>
)
}
export default TourCreator;
Try passing pickingHotspot in your dependency array for useEffect.
Your event handler is attached to your element in the useEffect on componentDidMount because of the empty dependency array. This will only happen once and that old function will be used. That old function will close over the value of the previous state. You can attach your event handler again on every relevant state change by passing pickHotSpot in your dependency array.
It is also a recommended approach to keep all your relevant code inside the hook. You could have put your listener function inside your hook, and would have seen a missing dependency warning from one of your lint tools.
Also, if there is no specific reason for you to add event hanlder like this from javascript, then add inline usin JSX, like #MB__ suggested. That will be executed on every render so it should be correct. At any time only one eventhandler for the particular event will be attached.

Remove Items dynamically from Array of Objects

How do I remove an object from an array dynamically after clicking the "remove" button.
For example, in the below code the table has n rows. After clicking on a particular remove button, it should delete only that row. Just like a todo list.
But in my case entire table is getting deleted.
const [items,itemList]=useState([]);
const [companyName,setCompanyName]=useState('');
const [experience, setExperience]=useState();
//Adding Items to Array after clicking "Add" button
const handleClose=()=>{
itemList((old)=>{
return [...old,{companyName,experience}]
})
}
//Removing Items from Array After clicking Remove button
const removeItem=(index)=>{
itemList((old)=>{
return old.filter((arrEle,i)=>{
return (
i!==index
);
})
})
}
//Displaying Array of objects on UI
<Table striped bordered hover size="sm">
<thead>
<tr>
<th>Company Name</th>
<th>Experience</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{
items.map((item,index)=>{
return(
<tr>
<td>{item.companyName}</td>
<td>{item.experience}</td>
<td><button onClick={()=>{removeItem(index)}}>Remove</button></td>
</tr>
)
})
}
</tbody>
</Table>
Issue
The main issue in your code is that you've a bunch of buttons being rendered inside a form and nearly all of them don't specify a button type. The default for buttons is type="submit", so when any of them are clicked they are submitting the form and the form is taking the default submit action which also happens to reload the page. When the page reloads your app reloads and loses the local component state.
Button type attribute
The default behavior of the button. Possible values are:
submit: The button submits the form data to the server. This is the default if the attribute is not specified for buttons associated with a <form>, or if the attribute is an empty or invalid value.
reset: The button resets all the controls to their initial values, like <input type="reset">. (This behavior tends to annoy users.)
button: The button has no default behavior, and does nothing when pressed by default. It can have client-side scripts listen to the element's events, which are triggered when the events occur.
Solution
Explicitly specify the button types.
The delete button
<button
type="button" // <-- "button" type
onClick={() => {
removeItem(item);
}}
>
Remove
</button>
The form buttons to submit, reset, and open the modal
<Button variant="primary" type="submit"> <-- "submit" type
Submit
</Button>
<Button
variant="primary"
type="reset" // <-- "reset" type
style={{ margin: '0 20px' }}
>
Reset Form
</Button>
<Button
variant="primary"
type="button" // <-- "button" type
onClick={handleShow}
style={{ margin: '0 15px' }}
>
Add Experience
</Button>
Note: The submit button is still going to submit the form and since you've not any onSubmit callback on the Form component this button, as-is, will cause the page to reload. You will want to add a submit handler and call preventDefault on the onSubmit event object.
const submitHandler = e => {
e.preventDefault();
// handle form data or whatever
};
...
<Form onSubmit={submitHandler}>
...
Generally we should not remove any item from array based on it's index. So try to remove items based on unique values:-
const removeItem = item => {
itemList(oldList => {
return oldList.filter((arrEle, i) => arrEle.companyName !== item.companyName);
});
};
And pass item in removeItem function like below:-
onClick={() => removeItem(item)}
Add key to tr like below:-
<tr key={`${item.companyName}-${index}`}>
Working demo: https://stackblitz.com/edit/react-cxkbge

Failing to pass onClick function to different file in React

So I have two .js files (are they also called modules?). The first .js file is a class-based component. It has handleClick() as well as render(). It looks like this (I've actually removed a lot of the code to make it appear shorter here):
handleClick(event) {
event.preventDefault()
console.log('handleclick')
this.initializeFetchApiAndSetState()
}
//Helper Function
checkGuessForCorrectAnswer() {
console.log('correct answer!')
}
render() {
return (
<div className="container-main">
<MultipleChoices
onClick={this.handleClick}
data={this.state.guess1}
/>
<button
className='Submit'
onClick={this.handleClick}
>
Submit
</button>
</div>
)
}
The button above works fine in that I can click on it and it'll console log the word 'correct answer!'. But for some reason, when I try to pass onClick to the "MultipleChoices" file/module it doesn't console log 'correct answer!'. The MultipleChoices.js file looks like this:
import React from "react"
function MultipleChoices(props) {
return(
<div>
<div className="button-grid">
<button
className="btn"
value={props.data}
onClick={props.handleClick}
>
{props.data}
</button>
</div>
</div>
)
}
export default MultipleChoices
Why can the button activate onClick in the first file, but not when I try to pass onClick to the MultipleChoice.js (which also has a button)?
In your upper component, you need to replace the onClick property with a handleClick property.
<MultipleChoices
handleClick={this.handleClick}
data={this.state.guess1}
/>
Because inside the Multiple Choices component you are calling the handleClick method from the properties (which is not set)
In your parent component you have given name to your property as onClick, while you are trying to acces it in children component as prop.handleClick.

How do I change the attributes of an RMWC Component?

I have an RMWC Button element, and I want to change the icon attribute when an onClick event is fired.
<Button outlined icon={<CircularProgress />}
onClick={(e)=> {
// e.currentTarget.icon = '';
// console.log(e.currentTarget.icon)
// ??? :V
}}
>Export</Button>
More specifically, I'm trying to make the Button stop loading when the button is clicked :P
You could do with useState update method
const [load,setLoad] = useState(true);
<Button outlined icon={load ? <CircularProgress />:<SomeOtherIcon/>}
onClick={(e)=> setLoad(false)} >Export</Button>

Categories