[英]Passing models to components
使用hyperstack.org框架,在对正在渲染的模型进行突变时,如何减少渲染周期?
将要渲染的模型传递给使该模型发生突变的组件时,所有呈现该模型的组件都会在发生任何突变时重新渲染。 除非突变是按按键进行的,否则这很好,因为这意味着按按键重新渲染所有组件。
例如,如果我们有此表:
class UserIndex < HyperComponent
render(DIV) do
puts "UserIndex render"
BridgeAppBar()
UserDialog(user: User.new)
Table do
TableHead do
TableRow do
TableCell { 'Name' }
TableCell { 'Gender' }
TableCell { 'Edit' }
end
end
TableBody do
user_rows
end
end
end
def user_rows
User.each do |user|
TableRow do
TableCell { "#{user.first_name} #{user.last_name}" }
TableCell { user.is_female ? 'Female' : 'Male' }
TableCell { UserDialog(user: user) }
end
end
end
end
该组件(用于编辑和新建):
class UserDialog < HyperComponent
param :user
before_mount do
@open = false
end
render do
puts "UserDialog render"
if @open
render_dialog
else
edit_or_new_button.on(:click) { mutate @open = true }
end
end
def render_dialog
Dialog(open: @open, fullWidth: false) do
DialogTitle do
'User'
end
DialogContent do
content
error_messages if @User.errors.any?
end
DialogActions do
actions
end
end
end
def edit_or_new_button
if @User.new?
Fab(size: :small, color: :primary) { Icon { 'add' } }
else
Fab(size: :small, color: :secondary) { Icon { 'settings' } }
end
end
def content
FormGroup(row: true) do
TextField(label: 'First Name', defaultValue: @User.first_name.to_s).on(:change) do |e|
@User.first_name = e.target.value
end
TextField(label: 'Last Name', defaultValue: @User.last_name.to_s).on(:change) do |e|
@User.last_name = e.target.value
end
end
BR()
FormLabel(component: 'legend') { 'Gender' }
RadioGroup(row: true) do
FormControlLabel(label: 'Male',
control: Radio(value: false, checked: !@User.is_female).as_node.to_n)
FormControlLabel(label: 'Female',
control: Radio(value: true, checked: @User.is_female).as_node.to_n)
end.on(:change) do |e|
@User.is_female = e.target.value
end
end
def actions
Button { 'Cancel' }.on(:click) { cancel }
if @User.changed? && validate_content
Button(color: :primary, variant: :contained, disabled: (@User.saving? ? true : false)) do
'Save'
end.on(:click) { save }
end
end
def save
@User.save(validate: true).then do |result|
mutate @open = false if result[:success]
end
end
def cancel
@User.revert
mutate @open = false
end
def error_messages
@User.errors.full_messages.each do |message|
Typography(variant: :h6, color: :secondary) { message }
end
end
def validate_content
return false if @User.first_name.to_s.empty?
return false if @User.last_name.to_s.empty?
return false if @User.is_female.nil?
true
end
end
在每个按键上都会重新呈现基础表(来自第一个代码示例),原因是:
TextField(label: 'First Name', defaultValue: @User.first_name.to_s)
.on(:change) do |e|
@User.first_name = e.target.value
end
由于重新渲染的数量,这导致键入显示缓慢。
我应该为每个字段保留局部状态变量,然后仅在保存时对模型字段进行更改吗?
看起来您正在使用Material UI,它将动态调整表格大小以使其最适合内容。 因此,我怀疑正在发生的事情是您在对话框中编辑值时在MUI表中显示first_name
和last_name
的值。
因此,在键入每个字符时,MUI会不断重新计算MUI表列的大小。
这不仅会减慢速度,而且还会给人类用户带来不安。 它给人的印象是,所做的更改全部已经生效,甚至在您保存它们之前。
因此,是的,我认为最好的方法是在用户键入时不直接更新记录的状态,而是更新局部状态变量。 然后只有在用户保存时,您才更新实际记录。
我确实注意到您有defaultValue
,它指示“不受控制”的输入。 但是您要对输入中的每个更改做出反应,这就是“受控”行为。 我认为您可以将defaultValue
更改为value
。
class UserDialog < HyperComponent
param :user
before_mount do
@open = false
@first_name = @User.first_name
@last_name = @User.last_name
@is_female = @User.is_female
end
render do
puts "UserDialog render"
if @open
render_dialog
else
edit_or_new_button.on(:click) { mutate @open = true }
end
end
def render_dialog
Dialog(open: @open, fullWidth: false) do
DialogTitle do
'User'
end
DialogContent do
content
error_messages if @User.errors.any?
end
DialogActions do
actions
end
end
end
def edit_or_new_button
if @User.new?
Fab(size: :small, color: :primary) { Icon { 'add' } }
else
Fab(size: :small, color: :secondary) { Icon { 'settings' } }
end
end
def content
FormGroup(row: true) do
TextField(label: 'First Name', value: @first_name).on(:change) do |e|
mutate @first_name = e.target.value
end
TextField(label: 'Last Name', value: @last_name).on(:change) do |e|
mutate @last_name = e.target.value
end
end
BR()
FormLabel(component: 'legend') { 'Gender' }
RadioGroup(row: true) do
FormControlLabel(label: 'Male',
control: Radio(value: false, checked: !@is_female).as_node.to_n)
FormControlLabel(label: 'Female',
control: Radio(value: true, checked: @is_female).as_node.to_n)
end.on(:change) do |e|
mutate @is_female = e.target.value
end
end
def actions
Button { 'Cancel' }.on(:click) { cancel }
return unless ready_to_save?
Button(color: :primary, variant: :contained, disabled: (@User.saving? ? true : false)) do
'Save'
end.on(:click, &:save)
end
def save
@User.update(first_name: @first_name, last_name: @last_name, is_female: @is_female).then do |result|
mutate @open = false if result[:success]
end
end
def cancel
mutate @open = false
end
def error_messages
@User.errors.full_messages.each do |message|
Typography(variant: :h6, color: :secondary) { message }
end
end
def ready_to_save?
return false if @first_name.empty?
return false if @last_name.empty?
return false if @is_female.nil?
return true if @first_name != @User.first_name
return true if @last_name != @User.last_name
return true if @is_female != @User.is_female
end
end
事实证明,导致性能问题的原因是我没有将唯一键传递给列表中的项目。 React对此非常讲究,但这并不是您得到警告的地方。
我所要做的就是:
User.each do |user|
TableRow do
...
TableCell { UserDialog(user: user) }
end
end
至:
User.each do |user|
TableRow do
...
# this passes a unique key to each Component
TableCell { UserDialog(user: user, key: user) }
end
end
通过上述更改,所有内容在两个示例中均能完美运行(第一个示例是根据用户类型更新基础表,第二个示例由@catmando提供),其中,更改仅在保存时应用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.