繁体   English   中英

将模型传递给组件

[英]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_namelast_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM