sidzan sidzan - 1 year ago 66
Javascript Question

How to use Refs Properly in React js?

I am confused with how the react refs works.
The issue for me is, whenever I change the input select value,

function is called.

I then call actions to set the value using relevant APIs.

However, currently, whenever I change the value, the whole component refreshes and the refs value are set to undefined.

What am I doing wrong?

Please note I have included only relevant codes.

/** @jsx React.DOM */
'use strict'

var React = require('react')
var connect = require("react-redux").connect
var moment = require('moment')
var actions=require("../actions");
var Calendar=require("./calendar");
var utils=require("../utils");
var CartChangeQty = require('./cart_change_qty')
var Table = require('react-bootstrap').Table
var Input = require('react-bootstrap').Input
var Register = require('./register')
var GroceryCheckout = React.createClass({
getInitialState: function() {
return {provinces: [], postal_codes: []};

render: function() {
var day_slots=[];
if (this.props.grocery_cart) {
var delivery_date=this.props.grocery_cart.delivery_date;
if (!delivery_date) delivery_date=this.props.grocery_cart.delivery_slots[0][0];
_.each(this.props.grocery_cart.delivery_slots,function(obj) {
if (obj[0]==delivery_date) {
console.log("###### day_slots",day_slots);
return <div className="plr-grocery-checkout">
<a className="plr-anchor" id="checkout"></a>
<h2>Grocery Checkout</h2>
{function() {
if (!this.props.grocery_cart) return <p>Your grocery cart is empty.</p>;
if (!this.props.user_data) {
return <div>
Is this your first time ordering? <input type="radio" name="first_time" ref="first_time_yes" onClick={this.onchange_first_time.bind(this,true)}/> Yes <input type="radio" name="first_time" ref="first_time_no" onClick={this.onchange_first_time.bind(this,false)}/> No
{function() {
if (this.state.first_time==true) {
return <Register/>
} else if (this.state.first_time==false) {
return ///something
} else {
return <div>
<h4><i className="glyphicon glyphicon-home"> </i> Delivery Address</h4>
<Input type="select" onChange={this.update_cart} ref="ship_address" style={{width:'auto',padding:'inherit',height:'auto'}}>
{ {
return <option key={} value={}>{obj.address}</option>

<h4><i className="glyphicon glyphicon-calendar "> </i> Please select your preferred delivery time slot:</h4>
<Calendar />

<div className="form-group">
<label className="col-sm-2 control-label">Payment Method</label>
<div className="col-sm-6">
<Input type="select" onChange={this.update_cart} ref="pay_method" style={{width:'auto',padding:'inherit',height:'auto'}}>
{ {
console.log("********************** payment method",
return <option key={} value={}>{}</option>
<div className="form-group">
<label className="col-sm-2 control-label">Payment Amount</label>
<div className="col-sm-6">
<p>{this.props.grocery_cart.amount_total} ฿</p>
<h4><i className=" glyphicon glyphicon-list-alt"> </i> Other Information</h4>
<div className="form-horizontal">
<div className="form-group">
<label className="col-sm-2 control-label">Return Foam Box</label>
<div className="col-sm-6">
<input type="checkbox" onChange={this.update_cart}/>
<span style={{margin:10}}>For this delivery, would you like us to take back the foam box for recycling?</span>
<div className="form-horizontal">
<div className="form-group">
<label className="col-sm-2 control-label">No Call</label>
<div className="col-sm-6">
<input type="checkbox" onChange={this.update_cart}/>
<span style={{margin:10}}>For this delivery, please do NOT call me one hour before delivery to re-confirm unless delayed</span>
<button className="btn btn-lg btn-primary" onClick={this.send_order} disabled={this.props.grocery_cart.amount_total<1500?true:false}><span className="glyphicon glyphicon-ok"></span> Send Order</button>
{this.props.sending_grocery_order?<span><img src="/static/img/spinner.gif"/> Sending order...</span>:null}
{function() {
if (this.props.grocery_cart.amount_total>=1500) return;
return <span className="plr-warning" style={{marginLeft:"20px"}}>Min. order: 1500 ฿!</span>

onchange_first_time: function(value) {
this.setState({first_time: value});

update_cart: function() {
var vals={
ship_address_id: this.refs.ship_address.value||null,
bill_address_id: this.refs.bill_address.value||null,
pay_method_id: parseInt(this.refs.pay_method.value),

onchange_qty: function(product_id,qty) {


var select=function(state) {
return {
grocery_cart: state.grocery_cart,
grocery_cart_loading: state.grocery_cart_loading,
user_data: state.user_data,
user_data_loading: state.user_data_loading,
sending_grocery_order: state.sending_grocery_order,


Answer Source

The reason your setup breaks down is probably because:

  • on_change_first_time() includes a setState(), which re-renders your component.
  • update_cart() dispatches an action. Probably this action triggers a new set of props for the component, causing the component to be re-rendered.

In both cases, your refs are probably preserved, but the values are not. Because they are not part of props, nor state. Because the props and state do not include value, react will empty the values upon re-rendering.

It is generally not good practice to read values from input components usings this.refs. In React, refs are for reading and updating DOM, but intended to be used only for stuff that you cannot do through pure react. Examples would be to read the height or width of HTMl components, or to add or remove event listeners to DOM components.

In your case, your update_cart() sends all values to some sort of other function, which presumably stores them somewhere.

I would advise:

  • put all values of all inputs in props, and pass them to the component.
  • in your render function, give all your input components a value={} value or similar.

That way, after your cart is sent of and updated, the component will be re-rendered with the new values.

Optionally, you could include optimistic rendering, by adding:

  • in getInitialState(), copy all your prop values to state values (as initial state of the component).
  • include parameters in input fields like value={}
  • in update_cart(), after your dispatch, add a setState() to update the state to the new input values.