React onClick event - reactjs

I'm missing something. Here's a very simple hello world, the goal is to just fire an alert event onClick. The event does fire when the page loads, but not when I click the button. I appreciate the help. Here's a jsFiddle to make it easier to see: jsFiddle
var Hello = React.createClass({
render: function() {
return <button onClick={alert("Hello World!")}>
Click Me
</button>;
}
React.render(<Hello />, document.getElementById('container'));

I think you're going about this wrong, because ReactJS is just JavaScript I don't think your way of firing this event will work. Your onClick should fire a function attached to your React element like so.
var Hello = React.createClass({
myClick: function () {
alert("Hello World!");
},
render: function() {
return <button onClick={this.myClick}>
Click Me
</button>;
}
});
React.render(<Hello />, document.getElementById('container'));

Note: this is another way to do it if you want something quick/inline:
<button onClick={()=>{ alert('alert'); }}>alert</button>

If the function to be run has parameters, is has to be bound to the function as follows:
var Hello = React.createClass({
handleClick: function (text) {
alert(text)
},
render: function () {
return <button onClick = {
this.handleClick.bind(null, "Hello World")
} > Click Me < /button>;
}
});
React.render(<Hello / > , document.getElementById('container'));
This makes sense now. Thanks again #Chris-Hawkes for pointing me in the right direction.

Now I see why I had the problem before. The problem came when I was trying to pass an argument to the function:
var Hello = React.createClass({
myClick: function (text) {
alert(text);
},
render: function() {
return <button onClick={this.myClick("Hello world")}>
Click Me
</button>;
}
});
This has the same behavior as the original code.

Related

Coordinating props of encapsulated components when rendering data in reactjs

I am building a recipe box in reactJS. My objective is hide ingredients within the button, listing the recipe title. Thus when a person clicks on a button titled "cheesecake" they will see its respective ingredients. The booleans in communicating when and when not to do this makes sense as it relates to "Onclick". However, I'm unsure of how to coordinate this action when fetching data given that my ingredients data (this.props.ingredients) is implicated within recipetitlebutton component. I tried re-initializing the ingredients component within the title button thinking that I can just define it within the recipeTitleButton when I mapped over the data. However, this didn't work and it didn't feel clean. Anyway, I hope this makes sense. Any help is greatly appreciated.
Thanks
var recipes = [{
recipe_title: "Cheesecake",
ingredients: "cream cheese, graham crackers, butter, eggs"
}, {
recipe_title: "Lasagna",
ingredients: " ricotta cheese, ground beef, pasta shells, parsely"
}, {
recipe_title: "Spaghetti",
ingredients: "noodles, pasta sauce, ground beef"
}]
var RecipeTitleButton = React.createClass({
getInitialState: function() {
return {
showIngredients: false
}
},
onClick: function() {
this.setState({
showIngredients: true
})
},
render: function() {
<Ingredients ingredients={this.props.ingredients}/>
return (
<div>
<button type="button" className="recipe_title_button" class="btn btn=primary btn-lg">{this.props.recipe_title}</button>
{this.state.showIngredients ? <Ingredients/>: null}
</div>
)
}
})
var Ingredients = React.createClass({
render: function() {
return (
<div id="ingredients" className="recipe_title_ingredients">
{this.props.ingredients}
</div>
)
}
})
var MainRecipeDisplay = React.createClass({
getInitialState: function() {
return {
recipeDataObject: recipes
}
},
render: function() {
var Pages = this.state.recipeDataObject.map(function(recipeContents) {
<RecipeTitleButton recipe_title={recipeContents.recipe_title} ingredients={recipeContents.ingredients}/>
})
return (
<div>
{Pages}
</div>
)
}
})
ReactDOM.render(<MainRecipeDisplay/>, document.getElementById('content'))
It appears that you're not actually passing the click handler to any of the elements. So you've defined the onClick method in RecipeTitleButton, but you're not passing this to anything. The solution is probably as simple as passing the <button> element a property onClick={/* The function that you want to fire on click */}.
First of all, change the name of the click handler to something like onClickHandler, for sanity. Click handlers are called with an event argument, which you probably don't need for your purposes but is important to know about (for example, if you need to prevent event propagation or have the click handler figure out which button was clicked).
Then the render function for RecipeTitleButton should look like:
render: function() {
<Ingredients ingredients={this.props.ingredients}/>
return (
<div>
<button type="button" className="recipe_title_button" onClick={this.onClickHandler.bind(this)} class="btn btn=primary btn-lg">{this.props.recipe_title}</button>
{this.state.showIngredients ? <Ingredients/>: null}
</div>
)
}
In case you're wondering, we bind the function to this so that the context remains the RecipeTitleButton component, instead of the window.
Oh, as an aside, it'd be easier to test this if you put it in a JSFiddle
Actually I figured it out. Was very simple. First, React doesn't recognize changing the setState value with a mere {setState({showingredients: true}). I had to use a function to make this explicit {setState(function(){return showIngredients:true)}. Second,in order to render the ingredients component within in my recipeTitle component, I only had to indicate props within {this.showIngredients ? :"null"/>. This way, I was allowed to define the ingredients prop within my recipeTitleButton. Code is Below:
var recipes = [{
title: "Cheesecake",
ingredients: "cream cheese, graham crackers, butter, eggs"
}, {
title: "Lasagna",
ingredients: " ricotta cheese, ground beef, pasta shells, parsely"
}, {
title: "Spaghetti",
ingredients: "noodles, pasta sauce, ground beef"
}]
var Layout = React.createClass({
getInitialState: function() {
return {
recipeDataObject: recipes,
showIngredients: false
}
},
render: function() {
var recipeContents = this.state.recipeDataObject.map(function(currentRecipe) {
return (
<RecipeTitleButton title={currentRecipe.title} ingredients={currentRecipe.ingredients}/>
)
})
return (
<div>
{recipeContents}
</div>
)
}
})
var RecipeTitleButton = React.createClass({
getInitialState: function() {
return {
showIngredients: false
}
},
handleRecipeButtonClick: function() {
**this.setState(function() {
return {
showIngredients: true
}**
});
},
render: function() {
return (
<div>
<button type="button" onClick={this.handleRecipeButtonClick.bind(this)} className="recipe_button">{this.props.title}</button>
**{this.state.showIngredients && <Ingredients ingredients={this.props.ingredients}/>}**
</div>
)
}
})
var Ingredients = React.createClass({
render: function(){
return(
<div className= "ingredients_list">
{this.props.ingredients}
</div>
)
}
})

How to bind event handler function dynamically in React

All:
I am pretty new to React, say I have a very simple case:
var React = require("react");
var ReactDOM = require("react-dom");
var Todo = React.createClass({
render: function() {
return (<div>Hello there
<button id="switch_func">Switch</button>
</div>);
}
});
ReactDOM.render(<Todo />, document.getElementById("div1"));
What I am trying to bind is:
There are multiple handler functions, each time when that button switch_func gets clicked, it will randomly choose another handlers and bind to itself.
But I do not know how to bind it like in AngularJS or jQuery, cos I am not sure if I can do same thing to the virtual DOM:
$("button#switch_func").on("click", function(){
$(this).off("click");
$(this).on("click", anotherHandler);
});
To bind functions you can use onClick
Simply put
<button onClick={this.handleClick}>Switch</button>
And handle the click event on the component's function:
var Todo = React.createClass({
handleClick: function(){
//put your function here!
},
//other functions in your component (e.g. render, getInitialState, etc)
});

Why is TestUtils.Simulate.click in Jest not working when used directly on React Components?

Let's say I have 2 components. A parent that contains a child.
The child component is a button like so:
var React = require('react');
var ChildButton = React.createClass({
onSubmitAnswer: function(e) {
this.props.onClick(this);
},
render: function() {
return (
<div className={this.props.visibility}>
<button onClick={this.onSubmitAnswer}>Click Me</button>
</div>
)
}
});
module.exports = ChildButton;
It lives within it's parent, which looks like this:
var React = require('react'),
ChildButton = require('./face-submit-button');
var ParentComponent = React.createClass({
onButtonSubmit: function() {
//Something happens here
},
render: function() {
return (
<div>
//Some more components
<ChildButton text="Submit" onClick={this.onButtonSubmit} />
</div>
)
}
});
module.exports = ParentComponent;
So far so good. Everything works as expected in the UI. But I've encountered some issues in the Jest tests using TestUtils.Simulate.click().
My test for the ChildButton component is straightforward and behaves as I would expect.
jest.dontMock('./child-button');
describe('ChildButton', function() {
var React = require('react/addons'),
ChildButton = require('./child-button'),
TestUtils = React.addons.TestUtils;
describe('events', function() {
var button,
onClickStub;
beforeEach(function() {
onClickStub = jest.genMockFn();
button = TestUtils.renderIntoDocument(
<ChildButton onClick={onClickStub} />
);
});
it('should call onSubmitAnswer when the button is clicked', function() {
var buttonTag = TestUtils.findRenderedDOMComponentWithTag(button, 'button');
TestUtils.Simulate.click(buttonTag);
expect(onClickStub).toBeCalled();
});
});
});
My test for the parent component started out looking the same:
jest.dontMock('./parent-component');
describe('ParentComponent', function() {
var React = require('react/addons'),
ParentComponent = require('./parent-component'),
ChildButton = require('./child-button'),
TestUtils = React.addons.TestUtils;
describe('events', function() {
var parent,
onClickStub;
beforeEach(function() {
onClickStub = jest.genMockFn();
parent = TestUtils.renderIntoDocument(
<ParentComponent onClick={onClickStub} />
);
});
it('should call onButtonSubmit when a click is triggered', function() {
var childButton = TestUtils.findRenderedComponentWithType(parent, ChildButton);
TestUtils.Simulate.click(childButton);
expect(onClickStub).toBeCalled();
});
});
});
But this test fails. The only difference I can see between these two tests is that one uses an HTML tag directly and clicks on it, while the other triggers a click on a React component. Can I not use the click event on React components directly? Is my assumption correct?
And if so, is there a way to trigger a click on React components differently in the tests? I tried using SimulateNative but that had the same effect, the onClickStub doesn't get called on click.
There is currently an open bug for this issue: Let ReactTestUtils.Simulate.click work on non-dom components. So the answer is that due to bugs, you can only use Simulate.click on an actual DOM node. So you can workaround the bug by getting the DOM node until it is fixed.

Add event handler to React.DOM element dynamically

I'm working with a RadioButtonGroup component which is like radio input but with buttons:
It would be good if using the component was easy like this:
var SelectThing = React.createClass({
render: function render() {
// I would not like to add onClick handler to every button element
// outside the RadioButtonGroup component
return (
<RadioButtonGroup onChange={this._onActiveChange}>
<button>Thing 1</button>
<button>Thing 2</button>
<button>Thing 3</button>
</RadioButtonGroup>
)
},
_onActiveChange: function _onActiveChange(index) {
console.log('You clicked button with index:', index);
}
});
The actual question: How can I achieve that the most elegantly with React? (I found another similar question but it doesn't exactly answer to this).
My first intuition was to add and remove the onClick handlers inside the component to remove boiler plate code from the component's user. Another option that comes to my mind is to give e.g. <p> elements instead of button elements and put them inside button elements which would be created inside the RadioButtonGroup component. I don't like the latter that much because it doesn't make that much sense semantically compared to passing buttons.
Here's what the (obviously not working) component looks like now:
// Radio input with buttons
// http://getbootstrap.com/javascript/#buttons-checkbox-radio
var RadioButtonGroup = React.createClass({
getInitialState: function getInitialState() {
return {
active: this.props.active || 0
};
},
componentWillMount: function componentWillMount() {
var buttons = this.props.children;
buttons[this.props.active].className += ' active';
var self = this;
buttons.forEach(function(button, index) {
// How to dynamically set onclick handler for virtual dom
// element created inside JSX?
button.addEventListener('onClick', function(event) {
self._onAnyButtonClick(index, event);
}
});
},
componentWillUnmount: function componentWillUnmount() {
var buttons = this.props.children;
buttons.forEach(function(button, index) {
button.removeEventListener('onClick');
});
},
render: function render() {
return (
<div className="radio-button-group">
{buttons}
</div>
)
},
_onAnyButtonClick: function _onAnyButtonClick(index, event) {
this.setState({
active: index
});
this.props.onChange(index);
}
});
You don't want to mess with click handlers on each button, just listen for the click on the container. Then update the state based on which child is clicked.
Also, with React it's best to keep all of your DOM stuff in the render function. In this case, defining an element's class name.
Here's how this could work:
var RadioButtonGroup = React.createClass({
getInitialState: function getInitialState() {
return {
active: this.props.active || 0
};
},
clickHandler: function clickHandler(e) {
// Getting an array of DOM elements
// Then finding which element was clicked
var nodes = Array.prototype.slice.call( e.currentTarget.children );
var index = nodes.indexOf( e.target );
this.setState({ active: index });
},
render: function render() {
var buttons = this.children.map(function(child, i) {
if (i === this.state.active) child.props.className += ' active';
return child;
}, this);
return (
<div className="radio-button-group" onClick={ this.clickHandler }>
{ buttons }
</div>
)
}
});
To get an api like this (similar to <input/>), we need to use the cloneWithProps addon.
<RadioButtonGroup onChange={this.handleChange} value={this.state.selected}>
<button>Test 1</button>
<button>Test 2</button>
<button>Test 3</button>
</RadioButtonGroup>
All this does is take each child, add a click handler, and conditionally add a className of 'active' to it. You easily can (and should) modify it to take the active class name as a prop.
var RadioButtonGroup = React.createClass({
render: function(){
return <div>{React.Children.map(this.props.children, this.renderItem)}</div>
},
renderItem: function(button, index){
return React.cloneElement(button, {
className: this.props.value === index ? ' active ' : '',
onClick: function(){
this.props.onChange(index);
}.bind(this),
key: index
});
}
});
demo
If you don't want to use cloneWithProps, you could use a wrapper div, but styling may be a bit more complex.
renderItem: function(button, index){
return React.createElement('div', {
className: this.props.value === index ? ' active ' : '',
onClick: function(){
this.props.onChange(index);
}.bind(this),
key: index
}, button);
}
The reason everything uses index is because you're passing react elements, which are opaque. There's no clean way to get any data out of these buttons, but we do know their index because we're iterating over them using React.Children.map. An alternative api would look like this:
<RadioButtonGroup value={'test1'} onChange={fn} options={{
test1: <button>Test 1</button>,
test2: <button>Test 2</button>,
test3: <button>Test 3</button>
}} />
Here we can iterate over this.props.options, and pass the key to the onChange callback, and take e.g. 'test1' as a value prop.
This is my code .I do not know whether this is the best way ,as i started using react just 5 hrs ago. But it works fine.
var LeftPane = React.createClass({
getInitialState: function() {
return {list:[{name:"Item 1",isactive:1},
{name:"Item 2",isactive:0},
{name:"Item 3",isactive:0},
{name:"Item 4",isactive:0},
{name:"Item 5",isactive:0}]};
},
clickHandler: function(index) {
var current_list = this.state.list;
current_list.map(function(record,i){
if(record.isactive==1){
if(i!=index){
record.isactive =0;
}else{
return;
}
}else{
if(i==index){
record.isactive =1;
}
}
});
this.setState({data:current_list});
},
render: function() {
var active_item = "list-group-item active"
var non_active_item ="list-group-item";
var _this = this;
return (
<div className="list-group">
{this.state.list.map(function(record,i) {
if(record.isactive==1){
return <a className={active_item} onClick={_this.clickHandler.bind(null, i)}>{record.name}</a>
}else{
return <a className={non_active_item} onClick={_this.clickHandler.bind(null, i)}>{record.name}</a>
}
})}
</div>
</div>
)
}
});
I would not like to add onClick handler to every button element
React uses events delegation pattern, so you can freely adding as many onClick handlers as needs.
Another way, if you just want to make code clear, you may create your own button component, and pass _onActiveChange to it props, but i don't sure that it's necessary in your case.
And remember, that React works with synthetic events, and, in some cases, usage of setState within native event handlers may cause an unpredictable behaviour.

can we attach click handlers to custom child components

I was trying to add a click handler to my own child component. In react chrome extension I was able to see the click handler as well.
But the click itself didn't work - wondering what did I miss.
Sample Code:
...
render (
<MySampleComponent onClick={this.handler} />
);
...
MySampleComponent can take whichever props it wants; components don't automatically copy props to their children. If you want to be able to add an onClick handler to MySampleComponent, then you can support this in the definition of that component:
var MySampleComponent = React.createClass({
render: function() {
return <div onClick={this.props.onClick}>...</div>;
}
});
You can add the handler from the samecomponent or call it through props.
Below code looks for onClick param in props. If nothing is passed, then
it goes for default handler in the component(clickHandler).
var MySampleComponent = React.createClass({
clickHandler: function(){
// write your logic
},
render: function() {
return <div onClick={this.props.onClick || this.clickHandler}>...</div>;
}
});
and while using this in another component use it as below
...........
handler: function() {
// write your logic
},
render {
var self = this;
return (<MySampleComponent onClick={self.handler} />);
}
......

Resources