How to pass an event handler to a child component in React - javascript

I have a <Button /> component I've created in React that abstracts out some of the styling in my application. I am using it in two different contexts - one to submit a login form, and the other to navigate to the registration page (and probably other contexts in the future).
I am trying to figure out how to pass the event handlers from the parent component to the <Button />. I want to call an onSubmit handler for the login form, but an onClick handler for the navigation button. Is this possible?
I have tried calling the component like this:
<Button text={callToAction} style={styles.callToActionButton} onClick={() => FlowRouter.go("Auth")}/>
<Button text="Go!" style={styles.registerButton} onSubmit={() => this.register(this.state.user, this.state.password)}/>
I've also tried removing the arrow function, which just causes the functions to execute when the component is loaded:
// executes event handlers on page load
<Button text={callToAction} style={styles.callToActionButton} onClick={FlowRouter.go("Auth")}/>
<Button text="Go!" style={styles.registerButton} onSubmit={this.register(this.state.user, this.state.password)}/>

In general, you can forward the onClick handler to your button class by passing it as a property. You could this make a required prop by simply defining the propTypes for your button component.
As an example, I added a small snippet that shows how it works
var StyledButton = React.createClass({
propTypes: {
// the StyledButton requires a clickHandler
clickHandler: React.PropTypes.func.Required,
// and I guess the text can be seen as required as well
text: React.PropTypes.string.required
},
render: function() {
// as you are sure you have a clickHandler, you can just reference it directly from the props
return <button type="button" onClick={this.props.clickHandler}>{this.props.text}</button>;
}
});
var MyForm = React.createClass({
getInitialState() {
return {
clicked: 0
};
},
click() {
this.setState({clicked: this.state.clicked+1});
alert('ouch');
},
secondClickHandler() {
this.setState({clicked: 0});
alert(':(');
},
render() {
// your parent component simply sets which button
return <fieldset>
<div>
<StyledButton clickHandler={this.click} text="Click me" />
{ (this.state.clicked > 0) && <StyledButton clickHandler={this.secondClickHandler} text="Not again" /> }
</div>
</fieldset>;
}
});
ReactDOM.render(
<MyForm />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Also, you wouldn't in general use the submit method of a button, you would rather send the data received to a webservice, and handle any changes when the result is received. The submit kills the current website and needs to load everything anew, while with an ajax call, or a store, it can just wait for the result and then redirect the user based on the response

How we have handled this is we have a button component that renders an a tag and then we have a href prop and a onClick prop you can pass in. If its a link just pass in the href prop to the button and if you are wanting it to execute a function just pass it in an onClick prop and make sure it gets set on the a tag.
In the Button component we also setup a custom onClick function that looks like this:
_onClick: function(e) {
if (!this.props.onClick) {
return;
}
this.props.onClick(e);
}
and then on the a tag
<a href={this.props.href} onClick={this._onClick} />

Related

In stencil js, how can i check if button has been clicked in another component from a different class

I have a component called button.tsx, this components holds a function that does certain things when the button is clicked, this.saveSearch triggers the saveSearch() function.
button.tsx
{((this.test1) || this.selectedExistingId) &&
(<button class="pdp-button primary"
onClick={this.saveSearch}>{this.langSave}</button>)
}
In sentence.tsx i want to be able to see when this button is clicked and show a certain div if the user has clicked it.
sentence.tsx
{onClick={saveSearch} && (<div class="styles-before-arrow">{this.langConfirmSearchSaved}</div>)}
You have a few options:
You can attach a click event listener for the button component in sentence.tsx. Take note that this may be trickier if you are working with elements which are encapsulated in Shadow DOM:
addButtonLister(): void {
document.querySelector('.pdp-button')
.addEventListener('click'), (e) => {
// add your logic here.
});
}
You can use EventEmitter (https://stenciljs.com/docs/events#events). In your button.tsx, you can add this:
#Event({eventName: 'button-event'}) customEvent: EventEmitter;
Then add something like this on button's onClick:
emitEvent() {
customEvent.emit('clicked');
}
render () {
return <button onClick={this.emitEvent}>{this.langSave}</button>
}
then from your sentence.tsx, add an event listener to your button component:
// say your button component's tag is <button-component>
document.querySelector('button-component')
.addEventListener('button-event', (e) => {
// your logic here.
});
You can use Stencil Store, but depending on your overall use-case, I am not sure if this may be an overkill - https://stenciljs.com/docs/stencil-store#store-state

I get Cannot GET /[object%20Object] on button click in React

I am new to React and learning click events. But the issue I faced is that one button click I get "Cannot GET /[object%20Object]". Here is the code I am using:
class Engine extends React.Component {
render() {
let types = ["Diesel", "Gazoline"];
return (
<div>
<Car type={types} />
</div>
);
}
}
class Car extends React.Component {
open() {
console.log("You have clicked");
}
render() {
return (
<div>
<h1>
{this.props.type.map((item, index) => {
return <p key={index}>{item}</p>;
})}
</h1>
<button onClick={open}>Remove all</button>
</div>
);
}
}
const box = document.querySelector(".mir");
ReactDOM.render(<Engine />, box);
You need to use this.open, since it doesn't know exactly what you're referring to by open :
<button onClick={this.open}>Remove all</button>
If you were to have named the function, say, foo instead of open, then it wouldn’t have worked at all.
The reason why you're seeing the functionality is because it is using the default open command built into JavaScript, which opens a new page. Under the hood, clicking the button is calling open(e) where e is the button event. So it's trying to open a new page, but instead of it receiving a URL, it's receiving an object, and thus you're seeing the error you received.
Instead, you want to use the open that is defined in the class. In order to do that, you need to use this.open.
In addition, if you want to pass something to the function as an argument, you'll need to change it a bit.
You can change open to:
open = (myparam) => {
console.log("You have clicked");
console.log(myparam);
}
In order to bind this. Then you can do something like:
<button onClick={_ => this.open("foo")}>Remove all</button>
You need to bind the open function to component and also call it with 'this'.
Or you can make the arrow function.
<button onClick={this.open.bind(this)}
or
open = () => { ... }
<button onClick={this.open}

React component listen to grand-children events

I am working on a component library which includes ButtonBar and Button components.
The ButtonBar component takes as children some Buttons and then renders them in a nice layout.
So, the consuming code, in the most basic case, would look something like this:
<ButtonBar>
<Button>First Button</Button>
<Button>Second Button</Button>
<Button>Third Button</Button>
</ButtonBar>
One feature of ButtonBar is keeping track of the last clicked Button and giving it special styling.
To accomplish this, ButtonBar has code like this:
updateToggle(child) {
// update state based on child...
}
componentDidMount() {
this.children = React.Children.map(this.children, (child) => {
return React.cloneElement(child, {
press: () => {
child.props.press();
this.updateToggle(child);
}
})
});
}
This code is trying to tap into the Button's press event (which is called by Button on itself when it's clicked upon) and then calls ButtonBar's updateToggle function.
This is sort of like "extending" Button's press function.
Anyway, this pattern breaks down when the user does something like this:
<ButtonBar>
<Link to="/"><Button>First Button</Button></Link>
<Link to="/foo"><Button>Second Button</Button></Link>
<Link to="/bar"><Button>Third Button</Button></Link>
</ButtonBar>
Now that the direct child of ButtonBar is not a Button type, we don't have an easy way determine if a custom event happened on the Button.
Is there a different way ButtonBar should be interacting with it's children?
How about adding Link as a property of Button?
<ButtonBar>
<Button to='/'>First Button</Button>
<Button to="/foo">Second Button</Button>
<Button to="/bar>Third Button</Button>
</ButtonBar>
You could enforce that only Button's are passed as Childen to ButtonBar
{React.Children.map( this.props.children, (child, i) => {
if (child.type.name === Button.name)
React.cloneElement(child, {
press: () => {
child.props.press();
this.updateToggle(child);
}
})
})}

onClick function is running on another event listener

Have been playing around with react. Have two event listeners the input which listens onChange and the button which should push the value to the array when its clicked.
Here's the code:
import React, { Component } from 'react';
let arr = [];
class App extends Component {
constructor() {
super();
this.state = {text: 'default'}
}
update( e ) {
this.setState({text: e.target.value})
}
add ( value ) {
arr.push(value)
console.log(arr)
}
render() {
return (
<div>
<h1>{this.state.text}</h1>
<input onChange={this.update.bind(this)}/>
<button onClick={this.add(this.state.text)}>Save</button>
</div>
)
}
}
export default App
The problem that the add function is running on change. Can't really get why.
Any suggestions?
onChange() triggers update()
update() calls this.setState() which changes state.
A state change causes render() to be invoked to re-render according to new state.
Rendering <button onClick={this.add(this.state.text)}>Save</button> invokes add() every time render() runs.
In order to defer invoking add(), you can define a function which gets triggered by the click event, as was shown in another answer. Alternatively, you can achieve the same functionality by adding a class method which encapsulates the trigger functionality:
addText() {
this.add(this.state.text)
}
render() {
…
<button onClick={this.addText.bind(this)}>Save</button>
This may or may not work for you, but in the context of the example, given, this would work.
Change <button onClick={this.add(this.state.text)}>Save</button>
To <button onClick={() => this.add(this.state.text)}>Save</button>
In your variant function add firing when component is rendering, and when you call setState with onChange of input you call this re-render.
The problem is add(this.state.text) is called whenever render() is called. To avoid this, you do not need to send the state as parameter, all you need to do is
<button onClick={this.add}>Save</button
or if you want to send a parameter you should bind it
<button onClick={this.add.bind(this, this.state.text)}>Save</button>

How to call function defined in parent component in child component on KeyDown event?

var B = React.createClass({
render: function() {
return (
<div>
<input type="button" onClick={this.props.saveFunction} />
</div>
);
}
});
var A = React.createClass({
render: function() {
return (
<div>
<B saveFunction={this.save} />
</div>
);
},
save: function(){
//Save code
}
});
Hello,
I am working in facebook react js.
I have created two components wherein A is parent component and B is child component.
I have written Save function in A and passing its reference as "props" to component B.
I am passing this Save function reference to "onClick" event of button rendered from B and I also want to save the values when user press the ENTER button.
So, I am trying to pass the same reference to "onKeyDown" event of same button.
But, its not working.
Contstraint is: I can't shift save function from A to B.
Please let me know how I can achieve this.
Thanks in advance!
I you pass this.save function to child element it loses it's context (JavaScript tires to execute save function when keyword this points to B element).
Bind that function with .bind(this) like:
<B saveFunction={this.save.bind(this)} />

Categories