How to bind event handler function dynamically in React - reactjs

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)
});

Related

how to access methods of an instance of a React component?

Is there a way to call the methods that a React component defines internally?
I understand generally we want to be passing values around declaratively with props/data etc. However I am using some component libraries that have internal useful methods.
eg
var field = <AutoComplete/>;
field.setValue("ready"); // doesn't work
eg this method
https://github.com/callemall/material-ui/blob/master/src/auto-complete.jsx#L244-L248
in material-ui AutoComplete component.
You can not do this with virtual dom, since virtual dom is just a description of components to be created(actual component instance will be created or updated only when rendering).
But you can access component instances inside your react component after rendering using refs:
var Test = React.createClass({
getInitialState: function(){
return {value:0};
},
setValue: function(value){
this.setState({value:value});
},
render: function() {
return <div>Value {this.state.value}</div>;
}
});
var App = React.createClass({
render: function() {
return <div>
<Test ref="test"/>
<button onClick={()=>this.refs.test.setValue(1)}>1</button>
<button onClick={()=>this.refs.test.setValue(2)}>2</button>
<button onClick={()=>this.refs.test.setValue(3)}>3</button>
</div>;
}
});
var mountNode = document.getElementById('app');
ReactDOM.render(<App name="John" />, mountNode);
jsbin with code above: http://jsbin.com/kitehujaje/1/edit?js,output

ReactJS: best practice access input values when creating forms

I've been playing a bit with ReactJS and am really enjoying the framework.
I'm also trying to follow the rule of creating components that are stateless where possible.
I have a Settings component that includes a child SettingsForm and a SettingsWidget.
Settings holds all the states, and only pass it as props to the form and widget.
This works (and scales) well because when the state in Settings is updated, it propagates to all child components.
var Settings = React.createClass({
getInitialState: function() {
settings: {}
}
})
What I am not 100% sure on is the best practice when accessing input values on SettingsForm to pass it on to the parent component.
I know I can use refs and also two-way binding to accomplish this, but neither feel very "ReactJS-like".
Is there a better of way accomplishing this that I am unaware of? For the sake of completeness, I've included the relevant code in my SettingsForm component below
var SettingsForm = React.createClass({
getInitialState: function() {
return {
changed: false
}
},
handleChange: function(event) {
this.setState({changed: true})
this.props.handleChange(
this.refs.emailInputFieldRef.getDOMNode().value,
this.refs.firstNameInputFieldRef.getDOMNode().value
)
},
handleSubmit: function(event) {
event.preventDefault();
// Access and pass on input values to parent callback so state is updated
this.props.handleUpdate(
this.refs.emailInputFieldRef.getDOMNode().value,
this.refs.firstNameInputFieldRef.getDOMNode().value
)
this.setState(this.getInitialState());
},
...
}
For now there is a Mixin you can use to link the input values to the state, called LinkedStateMixin that is exactly what you are looking for...
var WithLink = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function() {
return <input type="text" valueLink={this.linkState('message')} />;
}
});
Then all you have to do is modify your handler functions on the parent component to take your inputs as variables, and pass that function down to the child component as a prop. When you want to handle the form, call that function in the props and send the state (bound with from the Mixin) as the variables.
React Docs - React Link

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.

can i use action in Flux to control routes, HOW?

I am writing a authentication module in Flux, actions : auth,auth_success,auth_error. I am thinking when action auth_error occur, the router will go to '/login'. When action, action, auth_success occur, the router will go to '/dashboard'.
But it seems to be wrong because action only goes to dispatcher. I don't know how to do route the callbacks. Maybe check the store value?
You have to mixin your React class with Route.Navigation object, for instace
/** #jsx React.DOM */
var React = require('react');
var Router = require('react-router')
, Navigation = Router.Navigation;
var UserStore = require('user-store');
var YourClass = module.exports = React.createClass({
mixins:[Navigation], //This is very important
getInitialState: function() {
return {};
},
componentWillMount: function(){
UserStore.addChangeListener(this._method);
},
componentWillUnmount: function(){
UserStore.removeChangeListener(this._method);
},
render: function() {
return (
<div>
</div>
);
},
_method: function() {
// From here you can call methods of Navigator Object
this.transitionTo('SomeRouteName'); //This method will render the specified <Route>
}
});
For further information you can check
https://github.com/rackt/react-router/blob/master/docs/api/mixins/Navigation.md
In order to change the route and according to flux architecture, you should call transitionTo from a callback of some User Store you should have.
I added an example to the code, you may customise it to your specific case.
Happy coding!

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