Skip to content
This repository was archived by the owner on Oct 19, 2018. It is now read-only.

Commit fab7a72

Browse files
committed
deprecates shallow compare per #156
1 parent 3495f4b commit fab7a72

File tree

4 files changed

+124
-43
lines changed

4 files changed

+124
-43
lines changed

lib/react/component.rb

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def self.included(base)
1717
base.include(Callbacks)
1818
base.include(Tags)
1919
base.include(DslInstanceMethods)
20+
base.include(ShouldComponentUpdate)
2021
base.class_eval do
2122
class_attribute :initial_state
2223
define_callback :before_mount
@@ -90,49 +91,6 @@ def component_will_receive_props(next_props)
9091
self.class.process_exception(e, self)
9192
end
9293

93-
def should_component_update?(native_next_props, native_next_state)
94-
State.set_state_context_to(self) do
95-
next_params = Hash.new(native_next_props)
96-
if respond_to?(:needs_update?)
97-
call_needs_update(next_params, native_next_state)
98-
else
99-
!!(props_changed?(next_params) || native_state_changed?(native_next_state))
100-
end.to_n
101-
end
102-
end
103-
104-
def call_needs_update(next_params, native_next_state)
105-
component = self
106-
next_params.define_singleton_method(:changed?) do
107-
@changing ||= component.props_changed?(self)
108-
end
109-
next_state = Hash.new(native_next_state)
110-
next_state.define_singleton_method(:changed?) do
111-
@changing ||= component.native_state_changed?(native_next_state)
112-
end
113-
!!needs_update?(next_params, next_state)
114-
end
115-
116-
def native_state_changed?(next_state)
117-
%x{
118-
var normalized_next_state =
119-
(!#{next_state} || Object.keys(#{next_state}).length === 0 || #{nil} == next_state) ? false : #{next_state}
120-
var normalized_current_state =
121-
(!#{@native}.state || Object.keys(#{@native}.state).length === 0 || #{nil} == #{@native}.state) ? false : #{@native}.state
122-
if (!normalized_current_state != !normalized_next_state) return(true)
123-
if (!normalized_current_state && !normalized_next_state) return(false)
124-
if (!normalized_current_state['***_state_updated_at-***'] ||
125-
!normalized_next_state['***_state_updated_at-***']) return(true)
126-
return (normalized_current_state['***_state_updated_at-***'] !=
127-
normalized_next_state['***_state_updated_at-***'])
128-
}
129-
end
130-
131-
def props_changed?(next_params)
132-
(props.keys.sort != next_params.keys.sort) ||
133-
next_params.detect { |k, _v| `#{next_params[k]} != #{@native}.props[#{k}]` }
134-
end
135-
13694
def component_will_update(next_props, next_state)
13795
State.set_state_context_to(self) { self.run_callback(:before_update, Hash.new(next_props), Hash.new(next_state)) }
13896
@props_wrapper = self.class.props_wrapper.new(Hash.new(next_props), @props_wrapper)
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
module React
2+
module Component
3+
#
4+
# React assumes all components should update, unless a component explicitly overrides
5+
# the shouldComponentUpdate method. Reactrb does an explicit check doing a shallow
6+
# compare of params, and using a timestamp to determine if state has changed.
7+
8+
# If needed components can provide their own #needs_update? method which will be
9+
# passed the next params and state opal hashes.
10+
11+
# Attached to these hashes is a #changed? method that returns whether the hash contains
12+
# changes as calculated by the base mechanism. This way implementations of #needs_update?
13+
# can use the base comparison mechanism as needed.
14+
15+
# For example
16+
# def needs_update?(next_params, next_state)
17+
# # use a special comparison method
18+
# return false if next_state.changed? || next_params.changed?
19+
# # do some other special checks
20+
# end
21+
22+
# Note that beginning in 0.9 we will use standard ruby compare on all params further reducing
23+
# the need for needs_update?
24+
#
25+
module ShouldComponentUpdate
26+
def should_component_update?(native_next_props, native_next_state)
27+
State.set_state_context_to(self) do
28+
next_params = Hash.new(native_next_props)
29+
# rubocop:disable Style/DoubleNegation # we must return true/false to js land
30+
if respond_to?(:needs_update?)
31+
!!call_needs_update(next_params, native_next_state)
32+
else
33+
!!(props_changed?(next_params) || native_state_changed?(native_next_state))
34+
end
35+
# rubocop:enable Style/DoubleNegation
36+
end
37+
end
38+
39+
# create opal hashes for next params and state, and attach
40+
# the changed? method to each hash
41+
42+
def call_needs_update(next_params, native_next_state)
43+
component = self
44+
next_params.define_singleton_method(:changed?) do
45+
component.props_changed?(self)
46+
end
47+
next_state = Hash.new(native_next_state)
48+
next_state.define_singleton_method(:changed?) do
49+
component.native_state_changed?(native_next_state)
50+
end
51+
needs_update?(next_params, next_state)
52+
end
53+
54+
# Whenever state changes, reactrb updates a timestamp on the state object.
55+
# We can rapidly check for state changes comparing the incoming state time_stamp
56+
# with the current time stamp.
57+
58+
# Different versions of react treat empty state differently, so we first
59+
# convert anything that looks like an empty state to "false" for consistency.
60+
61+
# Then we test if one state is empty and the other is not, then we return false.
62+
# Then we test if both states are empty we return true.
63+
# If either state does not have a time stamp then we have to assume a change.
64+
# Otherwise we check time stamps
65+
66+
# rubocop:disable Metrics/MethodLength # for effeciency we want this to be one method
67+
def native_state_changed?(next_state)
68+
%x{
69+
var current_state = #{@native}.state
70+
var normalized_next_state =
71+
!#{next_state} || Object.keys(#{next_state}).length === 0 || #{nil} == #{next_state} ?
72+
false : #{next_state}
73+
var normalized_current_state =
74+
!current_state || Object.keys(current_state).length === 0 || #{nil} == current_state ?
75+
false : current_state
76+
if (!normalized_current_state != !normalized_next_state) return(true)
77+
if (!normalized_current_state && !normalized_next_state) return(false)
78+
if (!normalized_current_state['***_state_updated_at-***'] ||
79+
!normalized_next_state['***_state_updated_at-***']) return(true)
80+
return (normalized_current_state['***_state_updated_at-***'] !=
81+
normalized_next_state['***_state_updated_at-***'])
82+
}
83+
end
84+
# rubocop:enable Metrics/MethodLength
85+
86+
# Do a shallow compare on the two hashes. Starting in 0.9 we will do a deep compare.
87+
88+
def props_changed?(next_params)
89+
Component.deprecation_warning(
90+
"Using shallow incoming params comparison.\n"\
91+
'Do a require "reactrb/deep-compare, to get 0.9 behavior'
92+
)
93+
(props.keys.sort != next_params.keys.sort) ||
94+
next_params.detect { |k, v| `#{v} != #{@native}.props[#{k}]` }
95+
end
96+
end
97+
end
98+
end

lib/reactrb.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
require 'react/observable'
2121
require 'react/component'
2222
require 'react/component/dsl_instance_methods'
23+
require 'react/component/should_component_update'
2324
require 'react/component/tags'
2425
require 'react/component/base'
2526
require 'react/element'

lib/reactrb/deep-compare.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# rubocop:disable Style/FileName
2+
# require 'reactrb/deep-compare' to get 0.9 deep compare behavior
3+
module React
4+
module Component
5+
module ShouldComponentUpdate
6+
7+
def props_changed?(next_params)
8+
next_params != props
9+
end
10+
11+
def call_needs_update(next_params, native_next_state)
12+
component = self
13+
next_params.define_singleton_method(:changed?) do
14+
next_params != props
15+
end
16+
next_state = Hash.new(native_next_state)
17+
next_state.define_singleton_method(:changed?) do
18+
component.native_state_changed?(native_next_state)
19+
end
20+
needs_update?(next_params, next_state)
21+
end
22+
end
23+
end
24+
end

0 commit comments

Comments
 (0)