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

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

Related

React select onchange doesn't work

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.

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.

ReactJS: Control a child state from the child and the parent

I have a rather simple problem and I'm not sure how to solve it with React's one way data flow.
Say you have a link in the parent that shows a modal
In the modal, you have an "X" that closes it.
I know I can change the state of the modal from the parent via props
// In the parent
<Modal display={this.state.showModal} />
// In the modal
<div className={this.props.display ? "show" : "hide"}>
<a className="close">×</a>
...
</div>
And I know how to close the modal, but not both. Not sure how to keep a state that is shared and controllable by both the parent and the child modal.
UPDATE
In trying to keep this as modular as possible, I think the React way would be to store the open/close logic in the modal variable.
var ParentThing = React.createClass({
...
render (
<Modal /> // How can I call this.open in the modal from here?
)
});
var Modal = React.createClass({
setInitialState: function() {
return {
display: false
}
},
close: function() {
this.setState({ display: false });
},
open: function() {
this.setState({ display: true });
},
render: function() {
return (
<div className={this.state.display ? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
I saw this method, but it seems to be a little more than I need to do here. Reactjs: how to modify child state or props from parent?
There are two ways to handle this kind of thing in React:
Make the child "controlled," just like a form input with a value and onChange property, where the owner of the input controls the input.
Make the child "uncontrolled," just like a form input without a value.
The second choice seems faster up front, but just like managing a collection of form inputs in React, the advantage to using fully controlled components becomes apparent as complexity builds and the need to fully describe your UI at any point and time increases. (See this excellent answer from FakeRainBrigand if you're curious exactly why controlled components is better than uncontrolled in most cases.)
However, just like form inputs, there's no reason your component can't be either controlled or uncontrolled. If the user passes a display and onClose property, like Austin Greco's answer, you have a controlled modal, and the parent fully decides when to show or hide the modal.
If the user doesn't, you can skip using the properties and instead delegate to internal state managed by public methods on the modal component:
var ParentThing = React.createClass({
...
render: function() {
return <Modal ref="modal" />;
},
handleSomeClick: function() {
this.refs.modal.open();
}
});
var Modal = React.createClass({
setInitialState: function() {
return {
display: false
}
},
close: function() {
this.setState({ display: false });
},
open: function() {
this.setState({ display: true });
},
render: function() {
return (
<div className={this.state.display ? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
If you like the idea of a controlled Modal component, but don't want to do all the boilerplate typing, you could even go so far as to implement something like the valueLink property for the Modal to simplify this pattern.
var ParentThing = React.createClass({
...
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return { showModal: false };
},
render: function() {
return <Modal displayLink={this.linkState("showModal")} />;
},
handleSomeClick: function() {
this.setState({showModal: true});
}
});
var Modal = React.createClass({
close: function() {
this.props.displayLink.requestChange(false);
},
render: function() {
return (
<div className={this.props.displayLink.value? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
(See my blog post on creating custom components that work with linkState/valueLink for more info.)
So now you get the benefit of using a fully parent-controlled Modal, but you've removed a portion of the boilerplate around creating a function that sets the value to false and passing it to the modal.
You could pass a callback as a prop to the child component:
// In the parent
<Modal display={this.state.showModal} onClose={this.closeModal} />
// In the modal
<div className={this.props.display ? "show" : "hide"}>
<a className="close" onClick={this.props.onClose}>×</a>
...
</div>
Then when you click the close button on the child, it will call the function of the parent

Can a React component manipulate an existing DOM node?

I'd like to add a bit of functionality to some existing server-rendered HTML. Let's say I have a form:
<form>
<input type="checkbox" /> Show details
<div class='details'>
<input type="text" name="first_name" />
<input type="text" name="last_name" />
</div>
</form>
The HTML is already generated on the server. I'm wondering, can I use a React component to, say, add/remove a hide class to the .details div whenever the checkbox is checked and unchecked?
I don't want React to re-render the form, since the rest of the page is already handled by the server.
There is a solution but I don't know if it is efficient :
You can use the dangerouslySetInnerHTML to create a wrapper over your existing DOM elements.
Consider for example you want to add a swapper on two already existing div elements (https://jsfiddle.net/gdoumenc/a86g58qz/):
// a wrapper just rendering the previous DOM children
var Wrapper = React.createClass({
render: function() {
return <div dangerouslySetInnerHTML={{__html: this.props.html}}/>;
}
});
// a simple swapper
var Swapper = React.createClass({
getInitialState: function() {
return {swap: false};
},
onClick: function() {
this.setState({swap: !this.state.swap});
},
replace: function(id) {
if (!(id in this)) {
var node = document.getElementById(id);
var elt = document.createElement(node.tagName);
elt.appendChild(node.childNodes[0]);
this[id] = elt.innerHTML;
}
return this[id];
},
render: function() {
// replace here the elements par wrapped ones
box1 = <Wrapper html={this.replace('box1')}/>;
box2 = <Wrapper html={this.replace('box2')}/>;
if (this.state.swap) {
content = [box1, box2];
} else {
content = [box2, box1];
};
return <div>
{content}<button onClick={this.onClick}>Swap</button>
</div>;
}
});
`
Check server-rendering React example. You can see there that PHP script is getting React render result from node.js and returns it to client and then the same React component is attached to DOM for further modification.
If you want to have HTML rendered on server side and then handled by React that's the best approach. Otherwise you will need to write templates twice: in React and your server side template engine.

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