Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 25.2k
BREAKING - RCTEvent improvements, remove deprecated [sendInputEventWithName:body:]#15894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
0ecb4eedf92f4ac58ea62a72e0f4424e96aa7d306c5f998b217ecba04c6961e7bf91d5File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affilities. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
| #import <React/RCTEventDispatcher.h> | ||
| /** | ||
| * Generic untyped event for Components. Used internally by RCTDirectEventBlock and | ||
| * RCTBubblingEventBlock, for other use cases prefer using a class that implements | ||
| * RCTEvent to have a type safe way to initialize it. | ||
| */ | ||
| @interface RCTComponentEvent : NSObject<RCTEvent> | ||
| - (instancetype)initWithName:(NSString *)name viewTag:(NSNumber *)viewTag body:(NSDictionary *)body; | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to add some nonnull attributes to prevent crash? ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file could use | ||
| @end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /** | ||
| * Copyright (c) Facebook, Inc. and its affilities. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
| #import "RCTComponentEvent.h" | ||
| #import "RCTAssert.h" | ||
| @implementation RCTComponentEvent | ||
| { | ||
| NSArray *_arguments; | ||
| } | ||
| @synthesize eventName = _eventName; | ||
| @synthesize viewTag = _viewTag; | ||
| - (instancetype)initWithName:(NSString *)name viewTag:(NSNumber *)viewTag body:(NSDictionary *)body | ||
| { | ||
| if (self = [super init]) { | ||
| NSMutableDictionary *mutableBody = [NSMutableDictionary dictionaryWithDictionary:body]; | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait, that does not look right. Why do we need put tag inside body? That was needed for old poorly designed ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm pretty sure JS needs the tag in the body that's why we put it here. I could be wrong though I'd have to look at the RCTEventEmitter.receiveEvent code. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea it is definitely used https://github.com/facebook/react/blob/7f78749ee3023608a614b2f595739bcd36acc128/src/renderers/native/ReactNativeEventEmitter.js#L112. I'd rather not touch this :) Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah... But... I am trying understand that code... and I failed. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea I'm not sure why we are sending the tag as both the first arg and in the event body as target. I just think we should keep it like this to avoid breaking react. ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @shergin Let's see if someone from the react team can help here. I agree we should have a clear plan here. We also have to consider android if we make any change to the native / react bridging api. Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi! I'm only somewhat familiar with our event code. (I don't think anyone on the core team presently is deeply familiar with it, to be honest.) The event params like If the 2 values are always the same (for Android too) then it seems silly to always pass them both and I could update the Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW it looks like the Java implementation doesn't necessarily pass a ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @bvaughn, I'm pretty sure we need to keep target in the event payload otherwise it would be a breaking change for apps that rely on this value. Maybe we could avoir passing the tag as the first argument if it's always the same as body.target. Needs more investigation. @shergin What do you think about landing this as is since it will unblock a few other improvements I wanted to make?
| ||
| mutableBody[@"target"] = viewTag; | ||
| _eventName = RCTNormalizeInputEventName(name); | ||
| _viewTag = viewTag; | ||
| _arguments = @[_viewTag, _eventName, mutableBody]; | ||
| } | ||
| return self; | ||
| } | ||
| RCT_NOT_IMPLEMENTED(- (instancetype)init) | ||
| - (NSArray *)arguments | ||
| { | ||
| return _arguments; | ||
| } | ||
| - (BOOL)canCoalesce | ||
| { | ||
| return NO; | ||
| } | ||
| + (NSString *)moduleDotMethod | ||
| { | ||
| return @"RCTEventEmitter.receiveEvent"; | ||
| } | ||
| @end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -35,18 +35,31 @@ RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName); | ||
| @protocol RCTEvent <NSObject> | ||
| @required | ||
| @property (nonatomic, strong, readonly) NSNumber *viewTag; | ||
| @property (nonatomic, copy, readonly) NSString *eventName; | ||
| @property (nonatomic, assign, readonly) uint16_t coalescingKey; | ||
| - (BOOL)canCoalesce; | ||
| - (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent; | ||
| // used directly for doing a JS call | ||
| /** used directly for doing a JS call */ | ||
| + (NSString *)moduleDotMethod; | ||
| // must contain only JSON compatible values | ||
| /** must contain only JSON compatible values */ | ||
| - (NSArray *)arguments; | ||
| @optional | ||
| /** | ||
| * Can be implemented for view based events that need to be coalesced | ||
| * by it's viewTag. | ||
| */ | ||
| @property (nonatomic, strong, readonly) NSNumber *viewTag; | ||
| /** | ||
| * Coalescing related methods must only be implemented if canCoalesce | ||
| * returns YES. | ||
| */ | ||
| @property (nonatomic, assign, readonly) uint16_t coalescingKey; | ||
| - (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent; | ||
| @end | ||
| /** | ||
| @@ -81,12 +94,6 @@ __deprecated_msg("Subclass RCTEventEmitter instead"); | ||
| - (void)sendDeviceEventWithName:(NSString *)name body:(id)body | ||
| __deprecated_msg("Subclass RCTEventEmitter instead"); | ||
| /** | ||
| * Deprecated, do not use. | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have two usage of this in internal code base, which is cool and manageable! | ||
| */ | ||
| - (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body | ||
| __deprecated_msg("Use RCTDirectEventBlock or RCTBubblingEventBlock instead"); | ||
| /** | ||
| * Send a text input/focus event. For internal use only. | ||
| */ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10,8 +10,9 @@ | ||
| #import "RCTAssert.h" | ||
| #import "RCTBridge.h" | ||
| #import "RCTBridge+Private.h" | ||
| #import "RCTUtils.h" | ||
| #import "RCTComponentEvent.h" | ||
| #import "RCTProfile.h" | ||
| #import "RCTUtils.h" | ||
| const NSInteger RCTTextUpdateLagWarningThreshold = 3; | ||
| @@ -29,7 +30,7 @@ | ||
| static NSNumber *RCTGetEventID(id<RCTEvent> event) | ||
| { | ||
| return @( | ||
| event.viewTag.intValue | | ||
| ([event respondsToSelector:@selector(viewTag)] ? event.viewTag.intValue : 0) | | ||
| (((uint64_t)event.eventName.hash & 0xFFFF) << 32) | | ||
| (((uint64_t)event.coalescingKey) << 48) | ||
| ); | ||
| @@ -79,20 +80,6 @@ - (void)sendDeviceEventWithName:(NSString *)name body:(id)body | ||
| completion:NULL]; | ||
| } | ||
| - (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body | ||
| { | ||
| if (RCT_DEBUG) { | ||
| RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]], | ||
| @"Event body dictionary must include a 'target' property containing a React tag"); | ||
| } | ||
| name = RCTNormalizeInputEventName(name); | ||
| [_bridge enqueueJSCall:@"RCTEventEmitter" | ||
| method:@"receiveEvent" | ||
| args:body ? @[body[@"target"], name, body] : @[body[@"target"], name] | ||
| completion:NULL]; | ||
| } | ||
| - (void)sendTextEventWithType:(RCTTextEventType)type | ||
| reactTag:(NSNumber *)reactTag | ||
| text:(NSString *)text | ||
| @@ -110,7 +97,6 @@ - (void)sendTextEventWithType:(RCTTextEventType)type | ||
| NSMutableDictionary *body = [[NSMutableDictionary alloc] initWithDictionary:@{ | ||
| @"eventCount": @(eventCount), | ||
| @"target": reactTag | ||
| }]; | ||
| if (text) { | ||
| @@ -134,10 +120,10 @@ - (void)sendTextEventWithType:(RCTTextEventType)type | ||
| body[@"key"] = key; | ||
| } | ||
| #pragma clang diagnostic push | ||
| #pragma clang diagnostic ignored "-Wdeprecated-declarations" | ||
| [self sendInputEventWithName:events[type] body:body]; | ||
| #pragma clang diagnostic pop | ||
| RCTComponentEvent *event = [[RCTComponentEvent alloc] initWithName:events[type] | ||
| viewTag:reactTag | ||
| body:body]; | ||
| [self sendEvent:event]; | ||
| } | ||
| - (void)sendEvent:(id<RCTEvent>)event | ||
| @@ -149,34 +135,39 @@ - (void)sendEvent:(id<RCTEvent>)event | ||
| } | ||
| [_observersLock unlock]; | ||
| [_eventQueueLock lock]; | ||
| NSNumber *eventID = RCTGetEventID(event); | ||
| id<RCTEvent> previousEvent = _events[eventID]; | ||
| if (previousEvent) { | ||
| RCTAssert([event canCoalesce], @"Got event %@ which cannot be coalesced, but has the same eventID %@ as the previous event %@", event, eventID, previousEvent); | ||
| event = [previousEvent coalesceWithEvent:event]; | ||
| if (event.canCoalesce) { | ||
| [_eventQueueLock lock]; | ||
| NSNumber *eventID = RCTGetEventID(event); | ||
| id<RCTEvent> previousEvent = _events[eventID]; | ||
| if (previousEvent) { | ||
| event = [previousEvent coalesceWithEvent:event]; | ||
janicduplessis marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } else { | ||
| [_eventQueue addObject:eventID]; | ||
| } | ||
| _events[eventID] = event; | ||
| BOOL scheduleEventsDispatch = NO; | ||
| if (!_eventsDispatchScheduled) { | ||
| _eventsDispatchScheduled = YES; | ||
| scheduleEventsDispatch = YES; | ||
| } | ||
| // We have to release the lock before dispatching block with events, | ||
| // since dispatchBlock: can be executed synchronously on the same queue. | ||
| // (This is happening when chrome debugging is turned on.) | ||
| [_eventQueueLock unlock]; | ||
| if (scheduleEventsDispatch) { | ||
| [_bridge dispatchBlock:^{ | ||
| [self flushEventsQueue]; | ||
| } queue:RCTJSThread]; | ||
| } | ||
| } else { | ||
| [_eventQueue addObject:eventID]; | ||
| } | ||
| _events[eventID] = event; | ||
| BOOL scheduleEventsDispatch = NO; | ||
| if (!_eventsDispatchScheduled) { | ||
| _eventsDispatchScheduled = YES; | ||
| scheduleEventsDispatch = YES; | ||
| } | ||
| // We have to release the lock before dispatching block with events, | ||
| // since dispatchBlock: can be executed synchronously on the same queue. | ||
| // (This is happening when chrome debugging is turned on.) | ||
| [_eventQueueLock unlock]; | ||
| if (scheduleEventsDispatch) { | ||
| [_bridge dispatchBlock:^{ | ||
| [self flushEventsQueue]; | ||
| [self dispatchEvent:event]; | ||
| } queue:RCTJSThread]; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.