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

Platform.select method #7220

Closed
wants to merge 3 commits into from
Closed

Platform.select method #7220

wants to merge 3 commits into from

Conversation

grabbou
Copy link
Contributor

@grabbou grabbou commented Apr 25, 2016

Platform.select, given an object containing Platform.OS as keys, returns the value for the platform you are currently running on.

It works with styles:

var styles = StyleSheet.create({
  container: {
    flex: 1,
    ...Platform.select({
      ios: {
        backgroundColor: 'red',
      },
      android: {
        backgroundColor: 'blue',
      },
    }),
  },
});

as well as with functions:

var Component = Platform.select({
  ios: () => require('ComponentIOS'),
  android: () => require('ComponentAndroid'),
})();

<Component />;

The entire implementation :)

 var Platform = {
   OS: 'ios',
+  select: (obj: Object) => obj.ios,
 };

Kudos to @frantic for this amazing idea suggested in #7033

obj && typeof obj === 'object',
'Platform.select: Must be called with an object'
);
invariant(
Copy link
Contributor

Choose a reason for hiding this comment

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

Think we don't want this because you should be able to run code like this:

Platform.select({
  android: { ... }
})

and have the Android style get filtered out on iOS even if there are no iOS-specific styles.

Copy link
Contributor Author

@grabbou grabbou Apr 25, 2016

Choose a reason for hiding this comment

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

That's true, however the good thing is it can be used with non-style values and also at any level of StyleSheet object.

That means it's tricky to return a default value when Platform key is missing, like in the below example:

// will throw on Android, we can return an `{}` as a default, but then, the next example wont' work
Stylesheet.create({
   ...Platform.select({ 
      ios: {} 
   }) 
})

and

// here it might work, but it might be unexpected
StyleSheet.create({
   container: { 
     marginTop: Platform.select({
        ios: 5,
     })
   } 
})

and forgot to specify another platform.

My initial reasoning was that this is intended to be used when you have values for both platforms to support - otherwise you can just supply an empty object.

PS. I have just noticed that it's going to also throw when value is falsy (e.g. 0 given for marginTop, so that will have to be handled as well)

Copy link
Contributor

Choose a reason for hiding this comment

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

Agreed with @ide, I would remove that invariant. Not specifying an android block if you are on ios is okay.

@ghost
Copy link

ghost commented Apr 25, 2016

By analyzing the blame information on this pull request, we identified @emilioicai and @knowbody to be potential reviewers.

@ghost ghost added GH Review: review-needed CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels Apr 25, 2016
var Platform = {
OS: 'ios',
select: (obj: Object) => {
invariant(
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this one as well. This is going to be a hot path and the error isn't very useful.

@vjeux
Copy link
Contributor

vjeux commented Apr 25, 2016

This is going to be the most useful dummy one liner I've seen in a while :)

@vjeux vjeux self-assigned this Apr 25, 2016
@ide
Copy link
Contributor

ide commented Apr 25, 2016

Can you write jest tests too?

@grabbou
Copy link
Contributor Author

grabbou commented Apr 25, 2016

@ide sure, doing that now. Shall I also add a simple test for OS property?

render() {
return Platform.select({
ios: <ViewIOS />,
android: <ViewAndroid />,
Copy link
Contributor

@vjeux vjeux Apr 25, 2016

Choose a reason for hiding this comment

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

This is probably not going to work as you would expect. You're going to require the Android component on iOS and it may very well throw in this environment.

You'll likely want to write this as

var Component = Platform.select({
  ios: () => require('ComponentIOS'),
  android: () => require('ComponentAndroid'),
})();
return <Component />;

@vjeux
Copy link
Contributor

vjeux commented Apr 25, 2016

It cannot hurt :)

@vjeux
Copy link
Contributor

vjeux commented Apr 25, 2016

I'm happy with the diff. Any other input @ide?

returns the value for the platform you are currently running on.

```javascript
var { Platform } = React;
Copy link
Contributor

@vjeux vjeux Apr 25, 2016

Choose a reason for hiding this comment

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

actually, remove this, you're not doing the same for StyleSheet

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would you mind clarifying? (Is the PR update allowed when shipping in progress?)

Copy link
Contributor

Choose a reason for hiding this comment

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

Just remove this line, we would assume that people know how to require Platform.

For a shipping in progress, it'll only be picked up if I re-shipit.

We can just leave it for now, or kill it in another commit once it lands

@ide
Copy link
Contributor

ide commented Apr 25, 2016

🚢 🇮🇹

@vjeux
Copy link
Contributor

vjeux commented Apr 25, 2016

@facebook-github-bot shipit

@vjeux
Copy link
Contributor

vjeux commented Apr 25, 2016

Could you update the pull request description with the two examples that you added in the doc. I want to tweet about this and it'll make for a better landing page :)

@ghost ghost added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Apr 25, 2016
@ghost
Copy link

ghost commented Apr 25, 2016

Thanks for importing. If you are an FB employee go to Phabricator to review.

@grabbou
Copy link
Contributor Author

grabbou commented Apr 25, 2016

All righty. Should be done now.

@sebmarkbage
Copy link
Contributor

Does anyone have a good plan for dead code eliminating the unused platform?

@ghost ghost closed this in edf8888 Apr 25, 2016
@satya164
Copy link
Contributor

satya164 commented Apr 26, 2016

@sebmarkbage May be someone could write a babel plugin transform this code at build time instead of runtime, so that

Platform.select({
  ios: {
    backgroundColor: 'red',
  },
  android: {
    backgroundColor: 'blue',
  },
}),

becomes

{
  backgroundColor: 'red',
},

@jamiebuilds
Copy link

@satya164 here you go https://astexplorer.net/#/o5XwSweFRa

@satya164
Copy link
Contributor

Awesome @thejameskyle :D

cc @skevy should we include in our preset?

@abalhier
Copy link

abalhier commented May 3, 2016

Hello,

This feature seems great. Though I'm wondering why the way platform specific css rules is defined in the F8 app was not preferred :

var styles = StyleSheet.create({
  carousel: {
    ios: {
      margin: 10,
      overflow: 'visible',
      backgroundColor: 'black',
    },
  }
});

In F8StyleSheet.js :

export function create(styles: Object): {[name: string]: number} {
  const platformStyles = {};
  Object.keys(styles).forEach((name) => {
    let {ios, android, ...style} = {...styles[name]};
    if (ios && Platform.OS === 'ios') {
      style = {...style, ...ios};
    }
    if (android && Platform.OS === 'android') {
      style = {...style, ...android};
    }
    platformStyles[name] = style;
  });
  return StyleSheet.create(platformStyles);
}

From a user's perspective it would seem like there is less boilerplate code to write compared to using Platform.select and a spread operator. Do you think it's worth creating a pull request for this ?

@Bhullnatik
Copy link
Contributor

@abalhier You have the discussion leading to this decision on this PR : #7033

@abalhier
Copy link

abalhier commented May 3, 2016

Thank you @Bhullnatik :)

@vjeux
Copy link
Contributor

vjeux commented May 3, 2016

The gist of it is that we are trying to have decoupled concepts as much as possible. We don't want to tie in platform specific behavior with stylesheets. Chosing different things to do based on the platform is not limited to stylesheets and we'd rather have a specific api for it.

Now, we also need to make sure that developer experience is taken into account and verbosity is an important piece of it

@ide
Copy link
Contributor

ide commented May 3, 2016

Also this is something that is easy to do in user space without changes to the RN core so if you like F8StyleSheet's pattern, you should reimplement it and publish it to npm instead of sending PRs.

@knowbody
Copy link
Contributor

knowbody commented May 4, 2016

so I created this: https://github.com/knowbody/react-native-platform-stylesheet and published to npm so you can just: npm install react-native-platform-stylesheet

ptmt pushed a commit to ptmt/react-native that referenced this pull request May 9, 2016
Summary:
Kudos to frantic for this amazing idea! Works really well (yet so simple!)

Basically we had a discussion with vjeux and frantic and others in the PR facebook#7033 how to handle platform-specific stylesheets in a similar to F8 app way.

There were quite a few nice ideas there, however that one seems to be the smallest yet the most powerful.

Basically there's a `Platform.select` method that given an object, will select a `obj[Platform.OS]` value.

It works with styles:
`Platform.select({ ios: {}, android: {} })`

with messages:
`<Text>{Platform.select({ ios: 'Check the App Store', android: 'Check Google Play' })}</Text>`

and also works well with components (similar to Wallmart idea of <PlatformSwitch />) - relevant example included in diff.
Closes facebook#7220

Differential Revision: D3221709

Pulled By: vjeux

fb-gh-sync-id: 0a50071f2dcf2273198bc6e2c36e19bca97d7be9
fbshipit-source-id: 0a50071f2dcf2273198bc6e2c36e19bca97d7be9
@steida steida mentioned this pull request Jun 2, 2016
6 tasks
zebulgar pushed a commit to nightingale/react-native that referenced this pull request Jun 18, 2016
Summary:
Kudos to frantic for this amazing idea! Works really well (yet so simple!)

Basically we had a discussion with vjeux and frantic and others in the PR facebook#7033 how to handle platform-specific stylesheets in a similar to F8 app way.

There were quite a few nice ideas there, however that one seems to be the smallest yet the most powerful.

Basically there's a `Platform.select` method that given an object, will select a `obj[Platform.OS]` value.

It works with styles:
`Platform.select({ ios: {}, android: {} })`

with messages:
`<Text>{Platform.select({ ios: 'Check the App Store', android: 'Check Google Play' })}</Text>`

and also works well with components (similar to Wallmart idea of <PlatformSwitch />) - relevant example included in diff.
Closes facebook#7220

Differential Revision: D3221709

Pulled By: vjeux

fb-gh-sync-id: 0a50071f2dcf2273198bc6e2c36e19bca97d7be9
fbshipit-source-id: 0a50071f2dcf2273198bc6e2c36e19bca97d7be9
samerce pushed a commit to iodine/react-native that referenced this pull request Aug 23, 2016
Summary:
Kudos to frantic for this amazing idea! Works really well (yet so simple!)

Basically we had a discussion with vjeux and frantic and others in the PR facebook#7033 how to handle platform-specific stylesheets in a similar to F8 app way.

There were quite a few nice ideas there, however that one seems to be the smallest yet the most powerful.

Basically there's a `Platform.select` method that given an object, will select a `obj[Platform.OS]` value.

It works with styles:
`Platform.select({ ios: {}, android: {} })`

with messages:
`<Text>{Platform.select({ ios: 'Check the App Store', android: 'Check Google Play' })}</Text>`

and also works well with components (similar to Wallmart idea of <PlatformSwitch />) - relevant example included in diff.
Closes facebook#7220

Differential Revision: D3221709

Pulled By: vjeux

fb-gh-sync-id: 0a50071f2dcf2273198bc6e2c36e19bca97d7be9
fbshipit-source-id: 0a50071f2dcf2273198bc6e2c36e19bca97d7be9
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Import Started This pull request has been imported. This does not imply the PR has been approved.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants