Skip to content

Commit

Permalink
Introducting -[UIManager.setLocalData:forView:], the way to provide…
Browse files Browse the repository at this point in the history
… environmental data to ShadowView

Summary:
In some cases we need a way to specify some environmental data to shadow view
to improve layout (or do something similar), so `localData` serves these needs.
For example, any stateful embedded native views may benefit from this.
Have in mind that this data is not supposed to interfere with the state of
the shadow view.

Reviewed By: mmmulani

Differential Revision: D5884711

fbshipit-source-id: f0bf66e4608894ec4479b8aca262afcfba6b9f4b
  • Loading branch information
shergin authored and facebook-github-bot committed Sep 25, 2017
1 parent e5d4d49 commit 8b4ed94
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 0 deletions.
11 changes: 11 additions & 0 deletions React/Modules/RCTUIManager.h
Expand Up @@ -79,6 +79,17 @@ RCT_EXTERN NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplier
*/
- (void)setAvailableSize:(CGSize)availableSize forRootView:(UIView *)rootView;

/**
* Sets local data for a shadow view corresponded with given view.
* In some cases we need a way to specify some environmental data to shadow view
* to improve layout (or do something similar), so `localData` serves these needs.
* For example, any stateful embedded native views may benefit from this.
* Have in mind that this data is not supposed to interfere with the state of
* the shadow view.
* Please respect one-directional data flow of React.
*/
- (void)setLocalData:(NSObject *)localData forView:(UIView *)view;

/**
* Set the size of a view. This might be in response to a screen rotation
* or some other layout event outside of the React-managed view hierarchy.
Expand Down
14 changes: 14 additions & 0 deletions React/Modules/RCTUIManager.m
Expand Up @@ -339,6 +339,20 @@ - (void)setAvailableSize:(CGSize)availableSize forRootView:(UIView *)rootView
});
}

- (void)setLocalData:(NSObject *)localData forView:(UIView *)view

This comment has been minimized.

Copy link
@vovkasm

vovkasm Sep 25, 2017

Contributor

All this code needed to not write in view part couple lines of code?

    NSNumber *reactTag = view.reactTag;
    MyData myData = [self myData];
    RCTUIManager* uiManager = [self.bridge moduleForClass:[RCTUIManager class]];
    dispatch_async(RCTGetUIManagerQueue(), ^{
        MyShadowView *shadowView = (MyShadowView*)[uiManager shadowViewForReactTag:reactTag];
        RCTAssert(shadowView != nil, @"Could not locate MyShadowView with tag #%@", reactTag);

        shadowView.myData = myData;
        [uiManager setNeedsLayout];
    });

Not very good, because:

  1. All shadow views got unused slot 4/8 bytes
  2. Forced layout after set data
  3. Public interface to private data... you need check localData on you shadow views every time, because anyone can set it

It would be better to have more agile interface that allow to any communication with shadow views, something like:

    [uiManager forView:(RCTView*)view runCodeWithShadowView:^void (RCTShadowView* shadowView) {
        MyShadowView* myShadowView = (MyShadowView*)shadowView;
        myShadowView.myData = myData;
        [uiManager setNeedsLayout];
    }];

With that interface:

  1. No unused data slots in shadow views
  2. Programmer can control if layout needed
  3. No one can set private data to you shadow views
  4. Boilerplate with dispatch/queues still removed

This comment has been minimized.

Copy link
@shergin

shergin Oct 4, 2017

Author Contributor

That are good points!
I considered exact same approach before implementing this thing.
However I implemented this differently to have this API similar to what we have on Android, and because I tried to design it as conceptually "strict" as possible.
I plan to add executeBlockWithShadowView:forView: as an internal UIManager method though.

{
RCTAssertMainQueue();
NSNumber *tag = view.reactTag;

dispatch_async(RCTGetUIManagerQueue(), ^{
RCTShadowView *shadowView = self->_shadowViewRegistry[tag];
RCTAssert(shadowView != nil, @"Could not locate shadow view with tag #%@", tag);

shadowView.localData = localData;
[self setNeedsLayout];
});
}

/**
* TODO(yuwang): implement the nativeID functionality in a more efficient way
* instead of searching the whole view tree
Expand Down
12 changes: 12 additions & 0 deletions React/Views/RCTShadowView.h
Expand Up @@ -58,6 +58,18 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
@property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children
@property (nonatomic, copy) RCTDirectEventBlock onLayout;

/**
* In some cases we need a way to specify some environmental data to shadow view
* to improve layout (or do something similar), so `localData` serves these needs.
* For example, any stateful embedded native views may benefit from this.
* Have in mind that this data is not supposed to interfere with the state of
* the shadow view.
* Please respect one-directional data flow of React.
* Use `-[RCTUIManager setLocalData:forView:]` to set this property
* (to provide local/environmental data for a shadow view) from the main thread.
*/
@property (nonatomic, strong) NSObject *localData;

/**
* isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is
* set to NO in RCTUIManager after the layout pass is done and all frames have been extracted to be applied to the
Expand Down

0 comments on commit 8b4ed94

Please sign in to comment.