React select onchange doesn't work - reactjs

I am having problems with select tag implementation in React.js.
Here is what I have:
var ProfessorsFilter = React.createClass({
getInitialState: function(){
return(
{
visibleDepartments: [],
selectedSchoolId: 1
}
)
},
selectSchool: function(event){
console.log("select school");
alert("select ");
this.setState({selectedSchoolId: event.target.value});
},
render: function () {
var schoolOptions = this.props.schools.map(function(school, index){
return (
<option key={index} value={school.id}>{school.name}</option>
);
});
return(
<select onChange={this.selectSchool} value={this.state.selectedSchoolId} className="select-2">
{schoolOptions}
</select>
)
}
});
So, I am doing controlled component. But onchange does not fire my method selectSchool.
EDIT:
jsfiddle: https://jsfiddle.net/wc02bvaj/
BTW. I am also using select2.
EDIT2: It turned out to be problem with select2.

If you're using jQuery along with React on the same DOM elements it will generally cause problems. Either leave that unmanaged by React, use a React equivalent and remove jQuery, or in this case, you have a few options.
The easiest options is to just use https://github.com/rkit/react-select2-wrapper.
Alternatively you could roll your own solution, e.g.,
Select2 has an change callback on its own. If you're using Redux (it doesn't look like you are) you could change the state by dispatching an action from the Select2 change handler. You might be able to listen to the jQuery Select2 change event by registering a handler in your ProfessorsFilter and setting the state there.

Related

Why are component `refs` `undefined` when I try to access them?

I am new to React and I am using React v0.13.3 and JSXTransformer v0.13.3 to create a couple of simple components, each of which renders an input field along with a button inside a paragraph. When any button is clicked, I want to show the associated input value using an alert. I am trying to use refs to get the value, but for some reason it is not working, and shows undefined.
Here is my code:
var CommentBox = React.createClass({
show: function() {
alert(this.refs.test.value);
},
render: function() {
return(<p><input type="text" ref="test" /><button type="button" onClick={this.show}>Show</button></p>);
}
});
React.render(<div><CommentBox /><CommentBox /></div>, document.getElementById('commentbox'));
I would suggest to bind onChange of the input to set the value on the state, like so:
<input onChange={event => this.setState({value: event.target.value})} />
Now this.state.value always has the current value of the field. Then on the show function, just do:
show: function() {
alert(this.state.value);
}
Your code is working just fine! I put it in a jsfiddle.
However, that's not a good approach for your specific use-case. In general, you must try not to overuse refs. Here's a quote from the ReactJS related docs:
Your first inclination may be to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy.
So, here is a better approach:
For similar purposes, just like the one you need, using a controlled component is the preferred way. I suggest you to consider using your Component state.
Therefore, here's an example how you can achieve the same result, using the Component state. I am using your code snippet as a base:
var CommentBox = React.createClass({
getInitialState() {
return {
// That's the default input value
value: ''
};
},
show: function() {
alert(this.state.value);
},
handleChange: function(event) {
// Each time the input's value is changed, the state gets an update
this.setState({
value: event.target.value
});
},
render: function() {
return(
<p>
<input onChange={this.handleChange} type="text" />
<button type="button" onClick={this.show}>Show</button>
</p>
);
}
});
React.render(
<div><CommentBox /><CommentBox /></div>,
document.getElementById('commentbox')
);

react.js event listener on none react elements

I'm wondering, what's the best practice to add an event listener to a DOM element, which is NOT rendered by react (a radio button)
I have a given html form and I want to use the advantages of react.js instead of playing around with jQuery ajax stuff.
With jQuery I would do something like that:
$('input[type=radio][name=bedStatus]').change(function() {....}
Should I use something like this with react ()
componentDidMount: function() {
if (this.props.onWindowScroll) window.addEventListener("scroll", this.handleScroll);
},
componentWillUnmount: function() {
if (this.props.onWindowScroll) window.removeEventListener("scroll", this.handleScroll);
}
Could someone push me in the right direction?
Cheers Kai
For react elements created by your component:
render() {
return (
<div>
<input type="radio" onChange={this.handleChange} />
</div>
)
}
In terms of best practices, usually the non-react element should be a window or document. If it is a HTML element, then probably it should be wrapped in a react component and the event listener should be as above.
For non-react elements your approach is very good. It is very important to remove the listener when the component is unmounted.
Listeners are usually attached after the component is mounted:
componentDidMount: function() {
if (this.props.onWindowScroll) window.addEventListener("scroll", this.handleScroll);
}
Important: Remove when unmounted:
componentWillUnmount: function() {
if (this.props.onWindowScroll) window.removeEventListener("scroll", this.handleScroll);
}
I repeat that this is usually done to attach events on window or document objects.
Depending on your use case it can be acceptable to attach to HTML elements of parent or child elements or even of some elements created dynamically. findDOMNode() can be useful in some cases. But it is advised to avoid doing this whenever possible.

refs vs onChange

I've recently started learning react.js (loving it!) and ran into an interesting scenario with handling input values.
In the egghead tutorial they had you update an input using a ref like so:
handleSubmit(){
var newNote = this.refs.note.value;
this.refs.note.value = '';
this.props.addNote(newNote);
}
<input type="text" ref="note" />
<button type="button" onClick={this.handleSubmit.bind(this)}>
Submit
</button>
Later, I was playing with the material ui library (also awesome) and ran into a problem where I couldn't update one of the material components (Probably because of this issue) using a ref. After some time on Google I found that you could use a the state, and just update it using an onChange function, like so:
handleNoteChange(e){
this.setState({newNote: e.target.value});
}
<TextField
type="text"
value={this.state.newNote}
onChange={this.handleNoteChange.bind(this)} />
It seems like using a ref would be easier, however, in my recent time learning react native, everything you do seems to be the second way, using an onChange function and a state variable.
So my question is, moving forward, would it be better to use one over the other? Maybe there are limitations which make using the state better on native?
In React, avoid manipulating the DOM using references. You should never do anything like this:
this.refs.note.value = '';
It's fine to read from the DOM when necessary (usually in response to user input), but the only way information should pass from your application to the DOM is via your render() method. Otherwise, the DOM becomes out of sync with your application. In React, your "source of truth" lives in memory, not in the DOM.
The second example you provide is the standard React way. The view - as defined by the render() method - is ultimately derived from props and state. Changing the state triggers a change in view. So calling this.setState() will force your component to re-render.
I ran into the same situation while doing a Pluralsight tutorial on react. I thought I would share my experience and add another example as well. The tutorial altered the value of in input via it's ref value.
ReactDOM.findDOMNode(...)
Using ref shortens the code, but also alters the input value directly instead or relying on component state.
var Form = React.createClass({
onSubmit: function(e){
e.preventDefault();
var loginInput = ReactDOM.findDOMNode(this.refs.login);
this.props.addCard(loginInput.value);
loginInput.value = '';
},
render: function(){
return (<form onSubmit={this.onSubmit}>
<input placeholder="login" ref="login"/>
<button>Add</button>
</form>);
}
});
I modified the code to access and modify the input value via state.
var Form = React.createClass({
getInitialState: function(){
return {inputLogin: ''}
},
onKeyUpHandler: function(e){
this.setState({inputLogin: e.target.value})
},
onSubmit: function(e){
e.preventDefault();
this.props.addCard(this.state.inputLogin);
this.setState({inputLogin: ''})
},
render: function(){
return (<form onSubmit={this.onSubmit}>
<input placeholder="login" onKeyUp={this.onKeyUpHandler}/>
<button>Add</button>
</form>);
}
});
It's a little more verbose but now its using state properly.

react.js component: can't get keyDown events if component inside another one

JS Bin Available here
I have a component called ComponentB for which I want to get key down events:
var ComponentB = React.createClass({
render: function() {
return (
<div
contentEditable
onKeyDown={this.handleKeyDown}>{this.props.data}</div>
);
},
handleKeyDown: function(e) {
console.log("B: " + e.type +"-" + e.which);
}
});
I can get those events if this ComponentB is directly under the main/App component.
If I try to embed this component inside another component (componentA) I don't receive those events anymore (this.props.lines is an array of 3 strings) :
var ComponentA = React.createClass({
render: function(){
return (
<div
contentEditable>
{
this.props.lines.map(function(line,i) {
return <ComponentB key={i} data={line} />
})
}
</div>
);
}
});
The associated JS BIN shows this behavior : if you try to edit the lines in the component A section. no event will be emitted but they will if you edit the sinbgle instance of componentB below...
Looks to me like a bug in react.js but wanted to check here first.
As #ssorallen said in a comment, you can't have nested contentEditable elements, with or without react.
It seems to be a problem with nested contentEditable elements. Remove contentEditable from ComponentA, and it works as you expect.
One of the reasons this doesn't work, is because React doesn't really support contentEditable. Basically it sets the attribute, but it can't sensibly render into the contentEditable because when it tries to update it... the DOM has changed without its knowledge (which throws an error).
Rather you should treat contentEditable elements like an input element, which doesn't have children. Instead you update the innerHTML (see renderComponentToString and dangerouslySetInnerHTML) and listen to onInput events to get changes. See Stack Overflow: onChange event for contentEditable for details on how this works.
A proper handling of contentEditable has been discussed briefly, but no solution was arrived at. Feel free to suggest good ways to handle it.

How to change the state of a sibling component in ReactJS for a Master/Slave checkbox system?

I'm curious how to implement a Master/Slave checkbox system. The approach I am currently taking is to have an owner/ownee (parent/child) relationship between the Master/Slave checkboxes. However, I'm curious if there's a way to accomplish this in React if the checkboxes were siblings instead. I see here in the docs that it says to use your own global event system. Can someone please explain/show me an example of what they mean by this? Thanks!
In my Backbone+React application I use backbone events when I do not have any other options to communicate states between the components. I am pretty sure you can find other minimal event libraries or build your own to communicate events if you need to.
Below is an example code from the jsFiddle http://jsfiddle.net/kb3gN/2239/ ,I have supplied for your scenario.
From the 'MasterCheckbox' component's onChange event I trigger a global application wide 'checkbox:clicked' event on the event object that's accessible to other components/views.
//Global event object
window.App = _.extend({}, Backbone.Events);
//MasterCheckbox component's change handler
handleChange: function () {
this.setState({checked: this.refs.master.getDOMNode().checked});
App.trigger('checkbox:clicked', this.refs.master.getDOMNode().checked);
}
Then on the 'SlaveCheckbox' component I subscribe to that event and change the state of the
'SlaveCheckbox' component
componentDidMount: function(){
App.on('checkbox:clicked', function(state){this.setState({checked: state}) }.bind(this));
}
You could wrap the two checkboxes into their own component to make a MasterSlaveComponent. Wasn't sure of the exact functionality you would want, but here is an example where turning on Master also turns on Slave.
var MasterSlaveCheckbox = React.createClass({
getInitialState: function(){
return {
master: false,
slave: false
}
},
handleMasterChange: function() {
var newMasterState = !this.state.master;
if(newMasterState) {
this.setState({master: true, slave: true});
} else {
this.setState({master: false});
}
},
handleSlaveChange: function() {
this.setState({slave: !this.state.slave});
},
render: function() {
return <div>
<div>
<input type="checkbox" checked={this.state.master} onChange={this.handleMasterChange} />
Master
</div>
<div>
<input type="checkbox" checked={this.state.slave} onChange={this.handleSlaveChange} />
Slave
</div>
</div>;
}
});
https://jsfiddle.net/crob611/z6ozz62a/1/

Resources