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.
Related
I'm trying to create a simple app where if you click on a button, a modal overlay appears, and if you click on the 'x' in the modal it disappears.
I made a component for my button, called ShowOffer, and I have an onclick on it which toggles the boolean value of modalVisible, which is a piece of state.
However, nothing happens when I click on it.
I made another button element with the same onclick, and it seems to work fine.
Here is a code sandbox
You are adding onClick on the ShowOffer component, but here you are just passing it as a prop in it.
<ShowOffer display={"block"} onClick={toggleVisibility} />
is same as
React.createElement(ShowOffer, {
display: "block",
onClick: toggleVisibility
});
Under the hook, you are just passing an argument to a function
You have to add onClick event on the button in ShowOffer component as:
Live Demo
<button
style={{ display: `${display}` }}
onClick={toggleVisibility}
className="show-offer"
>
Show Offer
</button>
and you have to pass the toggleVisibility callback to ShowOffer as:
<ShowOffer display={"block"} toggleVisibility={toggleVisibility} />
This is the simple logic. Your ShowOffer component is not identify the onclick event and this component's button is not have any event handlers. So you just pass the event or directly pass the function name for access the event. Passing props name is the important one.
<ShowOffer display={"block"} onClick = {toggleBox}/>
export default function ShowOffer({ display, onClick}) {
return (
<button style={{ display: `${display}` }} className="show-offer" onClick={onClick}>
Show Offer
</button>
);
}
or
<ShowOffer display={"block"} toggleBoxFunct = {toggleBox}/>
export default function ShowOffer({ display, toggleBoxFunct }) {
return (
<button style={{ display: `${display}` }} className="show-offer" onClick={toggleBoxFunct}>
Show Offer
</button>
);
}
You can use concept of Callbacks,
App.js, make following changes,
pass toggleVisibility={toggleVisibility} as props, no need to mention onClick at component but at button
return (
<div className="App">
<Modal display={modalVisible ? "flex" : "none"} />
<ShowOffer display={"block"} onClick={toggleVisibility} toggleVisibility={toggleVisibility}/>
<button onClick={toggleVisibility}>Button</button>
</div>
);
ShowOffer.js, props passed function, call that function here with onclick,
import React from "react";
import "./ShowOffer.css";
export default function ShowOffer({ display, toggleVisibility }) {
return (
<button style={{ display: `${display}` }} onClick={toggleVisibility} className="show-offer">
Show Offer
</button>
);
}
working solution is here, https://codesandbox.io/embed/modal-overlay-tnis9?fontsize=14&hidenavigation=1&theme=dark
I am a beginner in React.js and I'm trying to implement a delete functionality for an app. On clicking the button, the form gets submitted and the function runs, however it shows an error in the console which is there for a split second so I couldn't read it. I have tried setTimeout to atleast see the error but it has no effect. The error just appears for a fraction of a second in the console and then disappears. The code is:
const Note = ({object}) => {
const {title, desc} = object;
const handleDel = (e, object)=>{
e.preventDefault();
console.log(object)
}
return (
<>
<div className="single-note">
<div>
<h1>{title}</h1>
<p>{desc}</p>
</div>
<form onSubmit={()=>handleDel(object)}>
<button type="submit" className="submit">
<FaRegTrashAlt/>
</button>
</form>
</div>
</>
)
}
export default Note
I guess you missed to pass the event and it is trying to invoke preventDefault on the object param that is passed. Try the below code:
onSubmt={(e) => handleDel(e,object)}
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.
Imagine I have a page "Parent" which conditionally renders a div "Child".
On the click of a button, "Child" opens. To close "Child" one has to click in a X button inside it.
This is how I would do it and in my opinion it looks clean.
const Parent = (props) => {
const [childVisible, setChildVisible] = useState(false);
return (
<>
{childVisible && <Child close={setChildVisible.bind(false)} />}
<button onClick={setChildVisible.bind(true)}>
Open Child
</button>
</>
)
}
const Child = (props) => {
return (
<div>
<p>Im Child</p>
<button onClick={props.close()}> X </button>
</div>
)
}
Since react v16.13.0 react has introduced a warning Warning: Cannot update a component from inside the function body of a different component. and it seems I can't do this anymore.
What's the correct pattern now? I would rather not have a state in both components stating the same thing.
Call back was not properly added .You could do like this onClick={props.close}
While use onClick={props.close()} like this. close() function run on child mount instead of click event
const Child = (props) => {
return (
<div>
<p>Im Child</p>
<button onClick={props.close}> X </button>
</div>
)
}
Given two components, one just being the render of a react-bootstrap ListGroup, and the other being a function containing a console.log print out, I'm running into trouble seeing that printout when using onClick in the listgroup item. I'm building a music player and the list group contains songs to click on.
When I try to remove <Player Player = /> from the ListGroupItem, it becomes a span element instead of a button. I could print a console.log from within the div but I was going to use the handleClick function to start working on song switching, and as the function is listed as undefined I'm kinda stuck.
In player.js (where the actual music player component is contained)
handleClick = (e) => {
e.preventDefault();
console.log('hello');
}
In Songlist.js
class Songlist extends Component {
constructor(props) {
super(props);
}
Songlist = () => {
return(
<div className="listgroup">
<ListGroup>
<ListGroupItem onClick=<Player Player = {this.handleClick} />>
button text goes here
</ListGroupItem >
//more list group items go here
</ListGroup>
</div>
);
}
render() {
return (
<div className="Songlist">
{this.Songlist()}
</div>
);
}
}
export default Songlist;
The actual error message I'm getting is "Error: Expected onClick listener to be a function, instead got a value of object type." But since I'm using an arrow function nothing needs to be bound, and I'm pretty sure that's the syntax for passing a function to an onClick listener no?
You need to do the following because onClick expects a function, instead of the object you are giving it.
<ListGroupItem onClick=<Player Player = {this.handleClick} />>
should become
<ListGroupItem onClick={() => <Player Player = {this.handleClick} />}>