i have a problem in ReactJS
I want to call this.props.onAction() method from Child class but i have an error:
Uncaught TypeError: this.props.onShotImageClick is not a function
http://jsfiddle.net/01fbL37L/
I want to call this method when buttons are generated dynamically in Parent class.
var btns = ['aaa', 'bbb', 'ccc']
var Child = React.createClass({
render: function () {
return <button onClick={this.handleClick} type="button"> {this.props.text} </button>;
},
handleClick: function () {
this.props.onAction("super text");
},
});
var Parent = React.createClass({
render: function () {
var buttons = btns.map(function(btn){
return <Child onAction={this.superAction} text={btn} />;
});
return <p>{buttons}</p>;
},
superAction: function (text) {
alert('The Child button say: ' + text);
}
});
React.renderComponent(<Parent />, document.body);
If i call it directly in "return" it works..
http://jsfiddle.net/f2ty4jkm/
Thanks.
Set this to .map callback, because in your case this refers to window not to your Parent object
var buttons = btns.map(function(btn) {
return <Child onAction={this.superAction} text={btn} />;
}.bind(this));
Example
Related
I have 2 files:
grid-body.jsx (GridBody) and grid-row.jsx (GridRow)
In GridBody, I declared a function showAlert which I pass to every GridRow:
var GridBody = React.createClass({
showAlert: function(msg) {
alert(msg);
},
render: function() {
var rows = this.props.rows.map(function(li) {
return (
<GridRow showAlert={this.showAlert} />
);
});
return (
<div>
{rows}
</div>
);
}
});
And in GridRow:
var GridRow = React.createClass({
toggle: function() {
this.props.showAlert('HEY'); // -----> ERROR - not a function
},
render: function() {
<div>
<a href="#" onClick={this.toggle} />
</div>
}
});
I'm trying to call the showAlert from parent and based on the examples I've seen, this is how to do it but I can't make it work.
you're using the wrong value for this inside of GridView.render. Either pass it explicitly to Array.map() (see the docs for how to do that) or assign this to some new variable at the very top of render() and reference that instead.
Here is a really, really great SO comment as to why this happens, as well as some other alternative workarounds if neither of the above work for you.
The context of the function passed to map in render method of GridBody is window and not the component. You can bind the interatee to get the behavior you want:
render: function() {
var rows = this.props.rows.map(function(li) {
return (
<GridRow showAlert={this.showAlert} />
);
}.bind(this));
return (
<div>
{rows}
</div>
);
}
I have a small problem where I have parent class and child class. I want to modify the state that was initialized in parent class so that I can see updated state in parent class. Here's the code:
var Parent = React.createClass({
getInitialState: function(){
return{
my_value: 0
}
},
_increaseValue: function(){
this.state.my_value++;
},
render: function(){
return(
<div><Child /></div>
)
}
});
var Child = React.createClass({
render: function(){
//at button I want to access _increaseValue function of parent
return (
<div>
<button onClick={_increaseValue}>Increase</button>
</div>
);
}
});
Now when user clicks the button in child class I would like to get the updated my_value in parent class, thus my questions are:
Is it possible?
If yes, how it is done?
Is this good practice or no?
Is it possible?
yes, it is possible
If yes, how it is done?
you can pass parent method to child through props, like so
var Parent = React.createClass({
getInitialState: function(){
return {
my_value: 0
}
},
onChangeValue: function () {
var value = this.state.my_value + 1;
this.setState({
my_value: value
})
},
render: function() {
return <div>
<Child
onChangeValue={ this.onChangeValue }
value={ this.state.my_value }
/>
</div>;
}
});
var Child = React.createClass({
_handleClick: function(){
this.props.onChangeValue();
},
render: function(){
return <div>
<h1> { this.props.value } </h1>
<button onClick={ this._handleClick }>Increase</button>
</div>
}
});
Example
Is this good practice or no?
It is good practice
You need to pass function via props into your child component. And when you need to change you call this function. It is normal practice and react way.
Example:
var Parent = React.createClass({
getInitialState: function(){
return{
my_value: 0
}
},
onChildClick: function() {
this.setState({
my_value: this.state.my_value + 1
})
},
render: function(){
return(
<div>
{this.state.my_value}
<Child onClick={this.onChildClick.bind(this)}/>
</div>
)
}
});
var Child = React.createClass({
_handleClick: function(){
this.props.onClick();
},
render: function(){
return (
<div>
<button onClick={this._handleClick}>Increase</button>
</div>
);
}
});
Example on JSFiddle
I'am creating component with input element and button element.
I need to get the input value and use with button, for example. How can I do that?
Here's my code:
var InputSearch = React.createClass({
getInitialState: function() {
return {
value: 'pics'
}
},
handleChange: function() {
this.setState({
value: event.target.value
});
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange} />
)
}
});
var ButtonSearch = React.createClass({
handleClick: function(event) {
console.log(this.state.value); // here's go the input value
},
render: function() {
return (
<button onClick={this.handleClick}>GO! </button>
)
}
});
var Search = React.createClass({
render: function() {
return (
<div>
<InputSearch />
<ButtonSearch />
</div>
)
}
});
React.render(
<Search />,
document.getElementById('result')
);
One issue here is that you are breaking a good rule - separate smart and dumb components. https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
The way to do this is to have a parent component that holds all the state and functionality of the children and passes all of this down as props...
//Our smart parent
var SearchContainer = React.createClass({
getInitialState : function() {
return {
value : 'pics'
}
},
handleInput : function(event) {
this.setState({value: event.target.value});
},
render : function() {
return (
<div>
<InputSearch value={this.state.value} onChange={this.handleInput} />
<ButtonSearch value={this.state.value} />
</div>
)
}
});
//Our dumb children
var InputSearch = React.createClass({
propTypes : {
onChange : React.PropTypes.func.isRequired,
value : React.PropTypes.string
},
render : function() {
return (
<input type="text" value={this.props.value} onChange={this.props.onChange} />
)
}
});
var ButtonSearch = React.createClass({
propTypes : {
value : React.PropTypes.string
},
handleClick : function() {
console.log(this.props.value); //log value
},
render : function() {
return (
<button onClick={this.handleClick}>GO! </button>
)
}
});
React.render(<Search />, document.getElementById('result'));
Here we pass the handler function down from parent to child so the input doesn't care what happens to the event it fires on change, it just needs to know that it has a prop called onChange that's a function and it invokes that.
The parent (SearchContainer) handles all of that functionality and passes the changed state down to both the button and the input...
hope that helps
Dan
You left out the event in your handleChange.
handleChange: function(event) {
this.setState({
value: event.target.value
});
},
The main architecture of react is the Parent Child / Master Slave principle.
If you want to pass values between components you have to create relations between.
Like for example
You create your master Component with few default states.
var MyMasterComponent = React.createClass({
getInitialState: function(){
...
},
render: function(){
return(
<ChilComponent1 textiwanttopass={this.state.text} />
);
}
});
With that method you are calling the render of another component within a master component. That way you can pass values from states into another component.
In that case you can access the passed text with this.props.textiwanttopass
I have a list with around 2k items. If I use onClick on each child, I would end up with 2k listeners which is what I have currently. I would want to do something like making the parent component listen to the click events instead. But if I do that, I don't have reference to the child component which I need to call setState on. Also the list of child components can be filtered dynamically (using this.refs might be bad ?).
The best I can come up with is to make a hash of child components id mapping to child components in the parent and look up the view on click.
Just for illustration purposes:
var Parent = React.createClass({
shouldComponentUpdate: function() { return false; },
handler: function(e) {
// look up from reference and set state
},
componentWillUnmount: function() {
// clean up reference
},
render: function() {
this.reference = {};
var items = [];
for(var i = 0; i < this.props.items.length; i++) {
var child = React.createClass(Child, {id: this.props.items[i].id});
items.push(child);
reference[child.id] = child;
}
return React.createClass('div', {onClick: this.handler}, items);
}
})
I wonder if there is a React way of dealing with this.
I think this answer may help... It does not matter if you have 2000 event handlers or just one. React deals with it in the same way. Remember that the HTML you return in your render method does not get added to the DOM but it is just used by React to build a virtual DOM representation. In the end, React has only one onClick.
React efficient way to bind event to many dom elements
If you need to know what element triggered the click you just need to access event.target and use any data-attribute to identify the clicked element.
The React way of doing this would be to use a Flux dispatcher + a Store. Basically, you can have each item bind to an event that gets triggered from the store once the store has carried out the tasks you want it to complete.
So the flow will be:
Item gets clicked => Flux event is dispatched => Flux dispatcher hears the events and executes the appropriate function with the data passed from Item component.
var ItemStore = {
doSomething: function(data){
// do something with the data
}
}
MicroEvent.mixin(ItemStore);
var AppDispatcher = new Dispatcher();
AppDispatcher.register(function(payload) {
switch (payload.eventName) {
case 'item-clicked':
ItemStore.doSomething(payload.data.someData);
ItemStore.trigger('did-something');
}
return true;
})
var Item = React.createClass({
shouldComponentUpdate: function() { return false; },
componentDidMount: function() {
ItemStore.bind('did-something', this.submitHandled);
},
handler: function(e) {
AppDispatcher.dispatch({
eventName: 'item-clicked',
data: {
someData: 'sample data'
}
});
},
componentWillUnmount: function() {
// clean up reference
},
submitHandled: function() {
// do something after the click
},
render: function() {
// insert your item's html here.
}
})
Building on #damianmr's answer, here's an example.
var Child = React.createClass({
shouldComponentUpdate(nextProps){
if (this.props.text !== nextProps.text) return true;
if (this.props.active !== nextProps.active) return true;
return false;
},
render(){
var className = 'Child';
if (this.props.active) className += ' Child-active';
return (
<div {...this.props} className={className}>
{this.props.text}
</div>
);
}
});
var Parent = React.createClass({
getInitialState(){
return {active: -1};
},
setActive(id){
this.setState({active: id});
},
render(){
return (
<div>
{this.props.items.map((item) => {
return (
<Child
active={this.state.active === item.id}
onClick={() => this.setActive(item.id)}
text={'My id is ' + item.id}
key={item.id}
/>
);
})}
</div>
);
}
});
as shown in the example below, I'd like MyComponent to dynamically attach an "onClick" event to its children. The onClick event should fire alertView that should be able to call the clicked element method "getValue".
JSFiddle: http://jsfiddle.net/2g638bp8/
How to do this? Thanks
var MyComponent = React.createClass({
alertValue: function () {
// RETRIEVE THE CHILD HERE
alert(child.getValue());
},
render: function () {
var children = React.Children.map(this.props.children, function (c, index) {
return React.addons.cloneWithProps(c, {
ref: 'child-' + index
});
});
return (
<div>
{children}
</div>
);
}
});
var MySubComponent = React.createClass({
getValue: function () {
return this.props.val;
},
render: function () {
return (
<div>{this.props.val}</div>
);
}
});
React.render(
<div>
<MyComponent>
<MySubComponent val="1" />
<MySubComponent val="2" />
<MySubComponent val="3" />
</MyComponent>
</div>,
document.getElementById('container')
);
You can't call methods on child components in React. You can only set properties. (The child is actually a ReactElement which contains information about the class and associated properties. It is not an instance of the component you created).
So, you could think about this a slightly different way and move the onClick to the MySubComponent:
var MyComponent = React.createClass({
onHandleGiveValue: function (value) {
alert(value);
},
render: function () {
const children = React.Children.map(this.props.children, child => React.cloneElement(child, { onGiveValue: this.onHandleGiveValue.bind(this) }));
return (
<div>
{children}
</div>
);
}
});
var MySubComponent = React.createClass({
handleClick: function() {
this.props.onGiveValue(this.props.val);
},
getValue: function () {
return this.props.val;
},
render: function () {
return (
<div onClick={ this.handleClick } >{this.props.val}</div>
);
}
});
React.render(
<div>
<MyComponent>
<MySubComponent val="1" />
<MySubComponent val="2" />
<MySubComponent val="3" />
</MyComponent>
</div>,
document.getElementById('container')
);
By doing that, your code can pass the current value as an event to the parent component. I've created a new event from the MySubComponent class named onGiveValue. That for now just passes the value from this.props.val. But, it could of course be anything.
Pass the parent callback to the subComponent, one dont need a reference for the child component.
React prefers composition design pattern, So your parent component should contains those three subComponent.
JS Fiddle: http://jsfiddle.net/68vt3umg/
var MyComponent = React.createClass({
handleChildClick: function (e, childValue) {
alert(childValue);
},
render: function () {
return (
<div>
<MySubComponent val="1" onSubClicked={this.handleChildClick}/>
<MySubComponent val="2" onSubClicked={this.handleChildClick}/>
</div>
);
}});
var MySubComponent = React.createClass({
getValue: function () {
return this.props.val;
},
handleOnClick: function (e, value) {
this.props.onSubClicked(e, this.props.val);
},
render: function () {
return (
<div onClick={this.handleOnClick}>{this.props.val}</div>
);
}
});
React.render(
<div>
<MyComponent />
</div>,
document.getElementById('container')
);