如何在 React js 中正确使用 Refs?
How to use Refs Properly in React js?
我对 react refs 的工作方式感到困惑。
我的问题是,每当我更改输入 select 值时,都会调用 update_cart
函数。
然后我使用相关 API 调用操作来设置值。
但是,目前,每当我更改值时,整个组件都会刷新并且 refs 值设置为未定义。
我做错了什么?
请注意,我只包含了相关代码。
/** @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() {
console.log("GroceryCheckout.render");
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) {
day_slots=obj[1];
}
}.bind(this));
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>
<p>
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
</p>
{function() {
if (this.state.first_time==true) {
return <Register/>
} else if (this.state.first_time==false) {
return ///something
} else {
return <div>
///something
<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'}}>
{this.props.user_data.contact_id.addresses.map(function(obj) {
return <option key={obj.id} value={obj.id}>{obj.address}</option>
})}
</Input>
<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'}}>
{this.props.grocery_cart.payment_methods.map(function(obj) {
console.log("********************** payment method",obj.name)
return <option key={obj.id} value={obj.id}>{obj.name}</option>
}.bind(this))}
</Input>
</div>
</div>
<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>
</div>
</div>
</div>
<hr/>
<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>
</div>
</div>
<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>
</div>
</div>
</div>
<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>
}.bind(this)()}
</div>
}
}.bind(this)()}
</div>
},
onchange_first_time: function(value) {
console.log("GroceryCheckout.onchange_first_time",value);
this.setState({first_time: value});
},
update_cart: function() {
console.log("GroceryCheckout.update_cart");
console.log("this.refs.pay_method.value",this.refs.pay_method.value);
var vals={
customer_id: this.props.user_data.contact_id.id,
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),
};
this.props.dispatch(actions.grocery_cart_update(vals));
},
onchange_qty: function(product_id,qty) {
console.log("GroceryItem.onchange_qty",product_id,qty);
this.props.dispatch(actions.grocery_cart_set_qty(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,
}
}
module.exports=connect(select)(GroceryCheckout);
您的设置出现故障的原因可能是:
on_change_first_time()
包含一个 setState()
,它会重新呈现您的组件。
update_cart()
调度一个动作。可能此操作会触发组件的一组新道具,从而导致重新渲染组件。
在这两种情况下,您的引用可能会保留,但值不会。因为它们不是道具的一部分,也不是状态的一部分。因为 props 和 state 不包含值,react 会在重新渲染时清空值。
使用 this.refs
从输入组件读取值通常 不是好的做法。在 React 中,refs
用于读取和更新 DOM,但旨在 仅用于 无法通过纯 React 完成的事情。例如读取 HTMl 个组件的高度或宽度,或者向 DOM 个组件添加或删除事件侦听器。
在你的情况下,你的 update_cart()
将所有值发送到某种其他函数,大概将它们存储在某个地方。
我会建议:
- 将所有输入的所有值放入 props 中,并将它们传递给组件。
- 在您的渲染函数中,为所有输入组件提供一个
value={this.props.foo}
值或类似值。
这样,在发送并更新您的购物车后,组件将使用新值重新呈现。
您可以选择包含乐观渲染,方法是添加:
- 在
getInitialState()
中,将所有道具值复制到状态值(作为组件的初始状态)。
- 在输入字段中包含参数,例如
value={this.state.foo}
- 在
update_cart()
中,在您发送后,添加 setState()
以将状态更新为新的输入值。
我使用的输入是反应 bootstrap
<Input type="select" onChange={this.update_cart} ref="ship_address" style={{width:'auto',padding:'inherit',height:'auto'}}>
{this.props.user_data.contact_id.addresses.map(function(obj) {
return <option key={obj.id} value={obj.id}>{obj.address}</option>
})}
</Input>
问题是每当调用 onchange 时再次调用 render 方法。
它重新呈现,因为我在这里调用设置状态。
解决此问题的一种方法是将输入放入不同的组件,它只是在更改时重新呈现该组件。
干杯。
我对 react refs 的工作方式感到困惑。
我的问题是,每当我更改输入 select 值时,都会调用 update_cart
函数。
然后我使用相关 API 调用操作来设置值。
但是,目前,每当我更改值时,整个组件都会刷新并且 refs 值设置为未定义。
我做错了什么?
请注意,我只包含了相关代码。
/** @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() {
console.log("GroceryCheckout.render");
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) {
day_slots=obj[1];
}
}.bind(this));
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>
<p>
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
</p>
{function() {
if (this.state.first_time==true) {
return <Register/>
} else if (this.state.first_time==false) {
return ///something
} else {
return <div>
///something
<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'}}>
{this.props.user_data.contact_id.addresses.map(function(obj) {
return <option key={obj.id} value={obj.id}>{obj.address}</option>
})}
</Input>
<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'}}>
{this.props.grocery_cart.payment_methods.map(function(obj) {
console.log("********************** payment method",obj.name)
return <option key={obj.id} value={obj.id}>{obj.name}</option>
}.bind(this))}
</Input>
</div>
</div>
<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>
</div>
</div>
</div>
<hr/>
<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>
</div>
</div>
<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>
</div>
</div>
</div>
<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>
}.bind(this)()}
</div>
}
}.bind(this)()}
</div>
},
onchange_first_time: function(value) {
console.log("GroceryCheckout.onchange_first_time",value);
this.setState({first_time: value});
},
update_cart: function() {
console.log("GroceryCheckout.update_cart");
console.log("this.refs.pay_method.value",this.refs.pay_method.value);
var vals={
customer_id: this.props.user_data.contact_id.id,
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),
};
this.props.dispatch(actions.grocery_cart_update(vals));
},
onchange_qty: function(product_id,qty) {
console.log("GroceryItem.onchange_qty",product_id,qty);
this.props.dispatch(actions.grocery_cart_set_qty(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,
}
}
module.exports=connect(select)(GroceryCheckout);
您的设置出现故障的原因可能是:
on_change_first_time()
包含一个setState()
,它会重新呈现您的组件。update_cart()
调度一个动作。可能此操作会触发组件的一组新道具,从而导致重新渲染组件。
在这两种情况下,您的引用可能会保留,但值不会。因为它们不是道具的一部分,也不是状态的一部分。因为 props 和 state 不包含值,react 会在重新渲染时清空值。
使用 this.refs
从输入组件读取值通常 不是好的做法。在 React 中,refs
用于读取和更新 DOM,但旨在 仅用于 无法通过纯 React 完成的事情。例如读取 HTMl 个组件的高度或宽度,或者向 DOM 个组件添加或删除事件侦听器。
在你的情况下,你的 update_cart()
将所有值发送到某种其他函数,大概将它们存储在某个地方。
我会建议:
- 将所有输入的所有值放入 props 中,并将它们传递给组件。
- 在您的渲染函数中,为所有输入组件提供一个
value={this.props.foo}
值或类似值。
这样,在发送并更新您的购物车后,组件将使用新值重新呈现。
您可以选择包含乐观渲染,方法是添加:
- 在
getInitialState()
中,将所有道具值复制到状态值(作为组件的初始状态)。 - 在输入字段中包含参数,例如
value={this.state.foo}
- 在
update_cart()
中,在您发送后,添加setState()
以将状态更新为新的输入值。
我使用的输入是反应 bootstrap
<Input type="select" onChange={this.update_cart} ref="ship_address" style={{width:'auto',padding:'inherit',height:'auto'}}>
{this.props.user_data.contact_id.addresses.map(function(obj) {
return <option key={obj.id} value={obj.id}>{obj.address}</option>
})}
</Input>
问题是每当调用 onchange 时再次调用 render 方法。 它重新呈现,因为我在这里调用设置状态。
解决此问题的一种方法是将输入放入不同的组件,它只是在更改时重新呈现该组件。
干杯。