Skip to content
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

Split layout props in GenericTouchable instead of using containerStyle #1617

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

janicduplessis
Copy link
Contributor

@janicduplessis janicduplessis commented Sep 1, 2021

Description

Touchable components do not layout as expected since they are wrapped in an extra view. To workaround this they have an extra containerStyle prop, but this is confusing and different from the ones in react-native.

This removes the containerStyle props and instead split the style prop passed between styles that should apply to the inner and outer view. This makes it seemlessly behave as if it was a single view.

Test plan

Using something like:

<View style={{ flexDirection: "row" }}>
  <TouchableOpacity
    style={{
      flex: 1,
      width: 50,
      height: 50,
      backgroundColor: "red",
      marginRight: 16,
      borderColor: "blue",
      borderWidth: 4,
      borderRadius: 6,
    }}
  />
  <TouchableOpacity
    feedback="opacity"
    sx={{
      flex: 1,
      width: 50,
      height: 50,
      backgroundColor: "red",
      marginRight: 16,
    }}
  />
  <TouchableOpacity
    feedback="opacity"
    sx={{
      flex: 1,
      width: 50,
      height: 50,
      backgroundColor: "red",
      marginRight: 16,
    }}
  />
</View>

Before:

image

After:

image

@@ -274,11 +272,10 @@ export default class GenericTouchable extends Component<
shouldActivateOnStart={this.props.shouldActivateOnStart}
disallowInterruption={this.props.disallowInterruption}
testID={this.props.testID}
{...coreProps}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does AnimatedBaseButton actually support all the accessibility props from coreProps? As far as I know this is the reason for using nested Views inside *Button components (docs).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea looks like you’re right, I think it would be possible to keep the extra view, but split the style between the 2 views to make sure it lays out as expected if it was only a single view.

For example the flex style needs to be on the outer view while flexDirection should be on the inner one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about doing the same thing when making my own touchable component, but turns out View has quite a few style props so I didn't bother in the end (here's the list).

This would definitely be a nice feature though if you can find a neat way to split the styles.

@janicduplessis janicduplessis changed the title Remove extra wrapper view from GenericTouchable Split layout props in GenericTouchable instead of using containerStyle Sep 2, 2021
@janicduplessis
Copy link
Contributor Author

@yfunk I was also doing something like this in my Touchable component. I've taken the code and revised it to make sure all styles are included. Let me know what you think about this approach.

Will see if I can add an example too to test complex layout cases.

@janicduplessis janicduplessis marked this pull request as draft September 9, 2021 15:08
@pontusab
Copy link

pontusab commented Sep 30, 2021

The reason for containerStyle is because of the extra <Animated.View />, right?

I solve this by removing the containerStyle altogether and instead of a View wrap the BaseButton with:

const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);

Then we can remove the View and therefore have the same behavior as in RN.

If you want I can create a PR with this so you can have a look, I think it's better to skip the containerStyle and just keep style as RN.

wdyt?

Copy link
Contributor

@yfunk yfunk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@janicduplessis Sorry for my late answer on this. I left a few comments, but overall the approach looks good to me 👍

@@ -0,0 +1,61 @@
import { FlexStyle, StyleProp, StyleSheet } from 'react-native';

const OUTER_PROPS: { [key in keyof FlexStyle]?: true } = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if these are really all the style props that need to be applied to the outer view or if there are more (e.g. backfaceVisibility, elevation, transform and all of ShadowStyleIOS).

It would probably be best to add complex layout examples to test this, as you mentioned.

@@ -261,10 +259,11 @@ export default class GenericTouchable extends Component<
onLayout: this.props.onLayout,
hitSlop: this.props.hitSlop,
};
const { outer, inner } = splitStyleProp(this.props.style);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The style prop will be split on every render (and also causes bridge traffic since the object changes). Not sure if this has any noticeable performance implications but that's something to keep in mind since some screens may contain a lot of touchables.

* </View>
* );
*/
export default function splitStyleProp<T extends FlexStyle>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be splitStyleProp<T extends ViewStyle> for better clarity.

@yfunk
Copy link
Contributor

yfunk commented Sep 30, 2021

The reason for containerStyle is because of the extra <Animated.View />, right?

I solve this by removing the containerStyle altogether and instead of a View wrap the BaseButton with:

const AnimatedBaseButton = Animated.createAnimatedComponent(BaseButton);

Then we can remove the View and therefore have the same behavior as in RN.

This was also the original approach of this PR, but unfortunately the extra Animated.View is needed to support accessibility props (see discussion above).

@jakub-gonet
Copy link
Member

@janicduplessis, what's the state on this?

@janicduplessis
Copy link
Contributor Author

I ended up hitting some edge cases where splitting props broke certain layouts, I don't remember exactly what, but will have to have another look to see if I can figure out something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants