hyper-react icon indicating copy to clipboard operation
hyper-react copied to clipboard

refs callbacks

Open leizzer opened this issue 8 years ago • 10 comments

I'm willing add a callback to a ref as they said in React documentation https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute

They said that the string refs could be deprecated.

What I was trying to achieve was something like this:

class Parent < React::Component::Base
  def change_child_state
    self.refs._child.state.lucky! true
  end

  def render
    Other(ref: ->{|c| self._child = child})
    button(value: 'Go').on :click do
      change_child_state
    end
  end
end

What @catmando kindly proposed me is:

module RefCallBacks
 param :on_ref, default: nil, allow_nil: true, type: Proc
 after_mount do
   params.ref(self)
 end
end

  class IllCallYou < React::Component::Base
    include RefCallBack
    ... go on with life
  end

  ... somewhere else
  IllCallYou(...).on(:ref) { |comp|  puts "comp = #{comp}" }

leizzer avatar Oct 04 '16 22:10 leizzer

I've tried by passing a Proc as ref, and it works as the original ref callback. So you could actually write like this

class Hello < React::Component::Base
  attr_accessor :my_span

  def modify_my_span
    if my_span
      `#{my_span}.innerHTML = 'bla'`
    end
  end

  def render
    div{
      button{ 'click me' }.on(:click) { modify_my_span }
      span(ref: ->(dom_node){ self.my_span = dom_node }) { "Hello World" }
    }
  end
end

Or even shorter by passing the setter method as ref,

span(ref: method(:my_span=).to_proc) { "Hello World" }

zetachang avatar Oct 05 '16 11:10 zetachang

This didn't work, it doesn't even puts 'hi' unless I remove the span in the render.

could it be because I am using reactrb-express?

def clicked
  puts 'hi'
  if my_span
    `#{my_span}.innerHTML = 'bla'`
  end
end

def render
  span(ref: ->(dom_node){ self.my_span = dom_node }) { "Hello World" }
  button do
    'Go'
  end.on :click do
    clicked
  end
end

leizzer avatar Oct 05 '16 17:10 leizzer

@zetachang what version were you trying this on? Perhaps this was in master?

catmando avatar Oct 05 '16 17:10 catmando

@leizzer are you doing this with reactrb-express? If so try again... I just realized that reactrb-express was about 5 versions behind reactrb. fixed now...

catmando avatar Oct 05 '16 18:10 catmando

It works guys... thank you a lot

leizzer avatar Oct 05 '16 23:10 leizzer

okay but I think this is still an issue. As ref is a kind of call back to be consistent we should add a callback method (like on, but for ref)

For example: .on(:mount) { |ref| ... } (nice and simple)

catmando avatar Oct 06 '16 17:10 catmando

I don't know.... doing it from ref respects React docs... But it would be nice if I don't need to embed js to use the dom_node.

Also reading the React docs says that if the element is an input, it returns the component instance but if it is other kind it returns the dom element... I didn't try it with inputs in reactrb

leizzer avatar Oct 06 '16 17:10 leizzer

@catmando , yeah, providing a callback would be consistent, but the ref callback is also called when unmount happen, so may be .on(:ref) { |dom_node_or_instance| ... }

zetachang avatar Oct 07 '16 02:10 zetachang

@leizzer - its just a syntactic difference. Unlike JS, ruby has several different ways to provide callbacks. In keeping with ruby philosophy it is good provide several (consistent) ways to get the job done, and let the programmer decide. BTW not sure what you mean't by "embed js" above examples do no have any js in them???

@zetachang - good catch on unmount. The on method now allows multiple names... so you can do .on(:mount, :unmount) if you want both, but I would guess in many cases the mount callback would want to be different than the unmount. So this way the programmer can decide, and i think it reads much better than :ref. what does that mean anyway?

catmando avatar Oct 07 '16 04:10 catmando

The ref callback is used to save the actual instance or dom node for further manipulate, the callback will be called after mounted, and called again with null when element is unmounted. So the usual use case is just as the above code sample: set & unset a property in a same callback given that you usually don't want to keep the DOM node if it's already unmounted.

Many people found the ref callback approach not intuitive, but this is actually a not bad solution so far given that string ref approach introduce ambiguity when the parent-child relationship is broken. For the discussion of the API change, see the original reactjs issue https://github.com/facebook/react/issues/3234

zetachang avatar Oct 07 '16 06:10 zetachang