Module: React::Component

Included in:
Components::HelloWorld, Components::Todo, TopLevelRailsComponent
Defined in:
lib/react/component.rb,
lib/react/component/api.rb,
lib/react/component/base.rb,
lib/react/component/class_methods.rb,
lib/react/component/props_wrapper.rb

Defined Under Namespace

Modules: API, ClassMethods Classes: Base, PropsWrapper

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(n, *args, &block) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/react/component.rb', line 229

def method_missing(n, *args, &block)
  return props[n] if props.key? n # TODO deprecate and remove - done so that params shadow tags, no longer needed
  name = n
  if name =~ /_as_node$/
    node_only = true
    name = name.gsub(/_as_node$/, "")
  end
  unless (HTML_TAGS.include?(name) || name == 'present'  || name == '_p_tag' || (name = component?(name, self)))
    return super
  end

  if name == "present"
    name = args.shift
  end

  if name == "_p_tag"
    name = "p"
  end

  React::RenderingContext.build_or_render(node_only, name, *args, &block)
end

Instance Attribute Details

#waiting_on_resourcesObject (readonly)

Returns the value of attribute waiting_on_resources.



259
260
261
# File 'lib/react/component.rb', line 259

def waiting_on_resources
  @waiting_on_resources
end

Class Method Details

.included(base) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/react/component.rb', line 15

def self.included(base)
  base.include(API)
  base.include(Callbacks)
  base.class_eval do
    class_attribute :initial_state
    define_callback :before_mount
    define_callback :after_mount
    define_callback :before_receive_props
    define_callback :before_update
    define_callback :after_update
    define_callback :before_unmount
  end
  base.extend(ClassMethods)

  if base.name
    parent = base.name.split("::").inject([Module]) { |nesting, next_const| nesting + [nesting.last.const_get(next_const)] }[-2]

    class << parent
      def method_missing(n, *args, &block)
        name = n
        if name =~ /_as_node$/
          node_only = true
          name = name.gsub(/_as_node$/, "")
        end
        begin
          name = const_get(name)
        rescue Exception
          name = nil
        end
        unless name && name.method_defined?(:render)
          return super
        end
        React::RenderingContext.build_or_render(node_only, name, *args, &block)
      end
    end
  end
end

Instance Method Details

#_render_wrapperObject



261
262
263
264
265
266
267
# File 'lib/react/component.rb', line 261

def _render_wrapper
  State.set_state_context_to(self) do
    React::RenderingContext.render(nil) {render || ""}.tap { |element| @waiting_on_resources = element.waiting_on_resources if element.respond_to? :waiting_on_resources }
  end
rescue Exception => e
  self.class.process_exception(e, self)
end

#childrenObject



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/react/component.rb', line 66

def children
  nodes = if `#{@native}.props.children==undefined`
    []
  else
    [`#{@native}.props.children`].flatten
  end
  class << nodes
    include Enumerable

    def to_n
      self
    end

    def each(&block)
      if block_given?
        %x{
              React.Children.forEach(#{self.to_n}, function(context){
        #{block.call(React::Element.new(`context`))}
              })
        }
        nil
      else
        Enumerator.new(`React.Children.count(#{self.to_n})`) do |y|
          %x{
                React.Children.forEach(#{self.to_n}, function(context){
          #{y << React::Element.new(`context`)}
                })
          }
        end
      end
    end
  end

  nodes
end

#component?(name) ⇒ Boolean

Returns:

  • (Boolean)


217
218
219
220
221
222
223
224
225
226
227
# File 'lib/react/component.rb', line 217

def component?(name)
  name_list = name.split("::")
  scope_list = self.class.name.split("::").inject([Module]) { |nesting, next_const| nesting + [nesting.last.const_get(next_const)] }.reverse
  scope_list.each do |scope|
    component = name_list.inject(scope) do |scope, class_name|
      scope.const_get(class_name)
    end rescue nil
    return component if component && component.method_defined?(:render)
  end
  nil
end

#component_did_mountObject



141
142
143
144
145
146
147
148
# File 'lib/react/component.rb', line 141

def component_did_mount
  State.set_state_context_to(self) do
    self.run_callback(:after_mount)
    State.update_states_to_observe
  end
rescue Exception => e
  self.class.process_exception(e, self)
end

#component_did_update(prev_props, prev_state) ⇒ Object



191
192
193
194
195
196
197
198
# File 'lib/react/component.rb', line 191

def component_did_update(prev_props, prev_state)
  State.set_state_context_to(self) do
    self.run_callback(:after_update, Hash.new(prev_props), Hash.new(prev_state))
    State.update_states_to_observe
  end
rescue Exception => e
  self.class.process_exception(e, self)
end

#component_will_mountObject



131
132
133
134
135
136
137
138
139
# File 'lib/react/component.rb', line 131

def component_will_mount
  IsomorphicHelpers.load_context(true) if IsomorphicHelpers.on_opal_client?
  @props_wrapper = self.class.props_wrapper.new(Hash.new(`#{@native}.props`))
  set_state! initial_state if initial_state
  State.initialize_states(self, initial_state)
  State.set_state_context_to(self) { self.run_callback(:before_mount) }
rescue Exception => e
  self.class.process_exception(e, self)
end

#component_will_receive_props(next_props) ⇒ Object



150
151
152
153
154
155
156
# File 'lib/react/component.rb', line 150

def component_will_receive_props(next_props)
  # need to rethink how this works in opal-react, or if its actually that useful within the react.rb environment
  # for now we are just using it to clear processed_params
  State.set_state_context_to(self) { self.run_callback(:before_receive_props, Hash.new(next_props)) }
rescue Exception => e
  self.class.process_exception(e, self)
end

#component_will_unmountObject



200
201
202
203
204
205
206
207
# File 'lib/react/component.rb', line 200

def component_will_unmount
  State.set_state_context_to(self) do
    self.run_callback(:before_unmount)
    State.remove
  end
rescue Exception => e
  self.class.process_exception(e, self)
end

#component_will_update(next_props, next_state) ⇒ Object



184
185
186
187
188
189
# File 'lib/react/component.rb', line 184

def component_will_update(next_props, next_state)
  State.set_state_context_to(self) { self.run_callback(:before_update, Hash.new(next_props), Hash.new(next_state)) }
  @props_wrapper = self.class.props_wrapper.new(Hash.new(next_props), @props_wrapper)
rescue Exception => e
  self.class.process_exception(e, self)
end

#define_state(*args, &block) ⇒ Object



255
256
257
# File 'lib/react/component.rb', line 255

def define_state(*args, &block)
  State.initialize_states(self, self.class.define_state(*args, &block))
end

#deprecated_params_method(name, *args, &block) ⇒ Object



61
62
63
64
# File 'lib/react/component.rb', line 61

def deprecated_params_method(name, *args, &block)
  self.class.deprecation_warning "Direct access to param `#{name}`.  Use `params.#{name}` instead."
  params.send(name, *args, &block)
end

#emit(event_name, *args) ⇒ Object



127
128
129
# File 'lib/react/component.rb', line 127

def emit(event_name, *args)
  self.params["_on#{event_name.to_s.event_camelize}"].call(*args)
end

#initialize(native_element) ⇒ Object



53
54
55
# File 'lib/react/component.rb', line 53

def initialize(native_element)
  @native = native_element
end

#p(*args, &block) ⇒ Object



209
210
211
212
213
214
215
# File 'lib/react/component.rb', line 209

def p(*args, &block)
  if block || args.count == 0 || (args.count == 1 && args.first.is_a?(Hash))
    _p_tag(*args, &block)
  else
    Kernel.p(*args)
  end
end

#paramsObject



102
103
104
# File 'lib/react/component.rb', line 102

def params
  @props_wrapper
end

#propsObject



106
107
108
# File 'lib/react/component.rb', line 106

def props
  Hash.new(`#{@native}.props`)
end

#props_changed?(next_props) ⇒ Boolean

Returns:

  • (Boolean)


158
159
160
161
# File 'lib/react/component.rb', line 158

def props_changed?(next_props)
  return true unless props.keys.sort == next_props.keys.sort
  props.detect { |k, v| `#{next_props[k]} != #{params[k]}`}
end

#refsObject



110
111
112
# File 'lib/react/component.rb', line 110

def refs
  Hash.new(`#{@native}.refs`)
end

#renderObject



57
58
59
# File 'lib/react/component.rb', line 57

def render
  raise "no render defined"
end

#should_component_update?(next_props, next_state) ⇒ Boolean

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/react/component.rb', line 163

def should_component_update?(next_props, next_state)
  State.set_state_context_to(self) do
    next_props = Hash.new(next_props)
    if self.respond_to?(:needs_update?)
      !!self.needs_update?(next_props, Hash.new(next_state))
    elsif false # switch to true to force updates per standard react
      true
    elsif props_changed? next_props
      true
    elsif `!next_state != !#{@native}.state`
      true
    elsif `!next_state && !#{@native}.state`
      false
    elsif `next_state["***_state_updated_at-***"] != #{@native}.state["***_state_updated_at-***"]`
      true
    else
      false
    end.to_n
  end
end

#stateObject



114
115
116
117
# File 'lib/react/component.rb', line 114

def state
  #raise "No native ReactComponent associated" unless @native
  @state_wrapper ||= StateWrapper.new(@native, self)
end

#update_react_js_state(object, name, value) ⇒ Object



119
120
121
122
123
124
125
# File 'lib/react/component.rb', line 119

def update_react_js_state(object, name, value)
  if object
    set_state({"***_state_updated_at-***" => Time.now.to_f, "#{object.class.to_s+'.' unless object == self}#{name}" => value})
  else
    set_state({name => value})
  end rescue nil
end

#watch(value, &on_change) ⇒ Object



251
252
253
# File 'lib/react/component.rb', line 251

def watch(value, &on_change)
  Observable.new(value, on_change)
end