React-native-web: Alert: implementation

Created on 7 Jul 2018  路  25Comments  路  Source: necolas/react-native-web

Most helpful comment

_very_ quick and dirty polyfill I whipped up if anyone else got super stuck by this:

import { Alert, Platform } from 'react-native'

const alertPolyfill = (title, description, options, extra) => {
    const result = window.confirm([title, description].filter(Boolean).join('\n'))

    if (result) {
        const confirmOption = options.find(({ style }) => style !== 'cancel')
        confirmOption && confirmOption.onPress()
    } else {
        const cancelOption = options.find(({ style }) => style === 'cancel')
        cancelOption && cancelOption.onPress()
    }
}

const alert = Platform.OS === 'web' ? alertPolyfill : Alert.alert

export default alert

Usage:

Before:

import { Alert } from 'react-native'
Alert.alert(
    ...
)

After:

import alert from './alert'
alert(
    ...
)

All 25 comments

@necolas can you please merge this changes.

For now I do something like this to workaround this.

const alertTitle = 'Foutmelding'
const alertText = 'Geen toegang tot locatiedata'
if (Platform.OS === 'web') {
   alert(alertText)
} else {
    Alert.alert(alertTitle, alertText)
}

Browser alert() is blocking, so it's not recomended to use. Not sure why it's not deprecated yet...

Does react-native-web supports Alert already?

What do you think about implementing it using react portals? We can inject the node in the dom and use a portal to show/hide.

We can ship simple styling for it and create a way to let the developer customize the appearance and the appearing/hiding transition.

What I've done so far.
one file for default react native alert, and one file for react web alert.
Alert. js

import { Alert } from 'react-native'

export const StdAlert = (title, desc, onPress = () => { }) => {
  Alert.alert(
    title,
    desc,
    [
      { text: 'OK', onPress: () => onPress() }
    ],
    { cancelable: false }
  )
}

export const BinaryAlert = (title, desc, onPositivePress = () => {}, onNegativePress = () => {}) => {
  Alert.alert(
    title,
    desc,
    [
      { text: 'Sim', onPress: () => onPositivePress() },
      { text: 'N茫o', onPress: () => onNegativePress() }
    ],
    { cancelable: false }
  )
}

and for web, Alert.web.js

export const StdAlert = (title, desc, onPress = () => {}) => {
  alert(`${title}\n${desc}`)
  if (onPress) onPress()
}

export const BinaryAlert = (title, desc, onPositivePress = () => {}, onNegativePress = () => {}) => {
  const res = window.confirm(`${title}\n${desc}`)
  if (res) onPositivePress()
  else onNegativePress()
}

Then use it as following:

import {StdAlert, BinaryAlert} from ...

StdAlert('title', 'desc', ()=>action())
BinaryAlert('title', 'desc', ()=>positiveAction(), ()=>negativeAction())

@vlimag , how about dependencies which use the Alert from react native ? (#1366)

@joan-saum As a provisory solution you should probably fork the dependency, change as necessary - following the example above - and publish to npm to use it.
Hopefully React native alert will be available at some time on React Native Web

I just created my own alert popup for web, using native-base - works pretty well

https://gist.github.com/tonypee/f3ebb3a6f89e6d73255a5823092b24c6

it needs to be instantiated in the root of the app too

_very_ quick and dirty polyfill I whipped up if anyone else got super stuck by this:

import { Alert, Platform } from 'react-native'

const alertPolyfill = (title, description, options, extra) => {
    const result = window.confirm([title, description].filter(Boolean).join('\n'))

    if (result) {
        const confirmOption = options.find(({ style }) => style !== 'cancel')
        confirmOption && confirmOption.onPress()
    } else {
        const cancelOption = options.find(({ style }) => style === 'cancel')
        cancelOption && cancelOption.onPress()
    }
}

const alert = Platform.OS === 'web' ? alertPolyfill : Alert.alert

export default alert

Usage:

Before:

import { Alert } from 'react-native'
Alert.alert(
    ...
)

After:

import alert from './alert'
alert(
    ...
)

Thank you @joshbalfour.

Here's roughly the same in TypeScript for those who need it. Written as a singleton in order to be able to implement AlertStatic symmetrically from React Native and call it via Alert.alert.

// Alert.web.ts
import { AlertButton, AlertStatic } from 'react-native';

class WebAlert implements Pick<AlertStatic, 'alert'> {
  public alert(title: string, message?: string, buttons?: AlertButton[]): void {
    if (buttons === undefined || buttons.length === 0) {
      window.alert([title, message].filter(Boolean).join('\n'));
      return;
    }

    const result = window.confirm([title, message].filter(Boolean).join('\n'));

    if (result === true) {
      const confirm = buttons.find(({ style }) => style !== 'cancel');
      confirm?.onPress?.();
      return;
    }

    const cancel = buttons.find(({ style }) => style === 'cancel');
    cancel?.onPress?.();
  }
}

export const Alert = new WebAlert();
// Alert.ts
export { Alert } from 'react-native';

Several things to note:

  1. Providing no buttons argument manifests a basic alert. Providing buttons presents a dialogue.
  2. The fourth options argument is not implemented as it pertains to Android cancellation behaviour only, which has no browser analogue.
  3. The AlertStatic interface implies an additional prompt method, but the React Native docs suggest that it hasn't yet been implemented (or that it's just undocumented, for now).

EDIT: Added Alert.ts to indicate how it should be imported.

Browser alert() is blocking, so it's not recomended to use. Not sure why it's not deprecated yet...

It's meant to block on purpose!

The Modal PR is merged in react-native-web, that means the Alert implementation will be a lot easier to implement. I'll see if I can find some time to make a PR for this

I just did an experiment with the Modal in the canary version to implement a the Alert API on the web: https://codesandbox.io/s/alert-implementation-z4eoq?file=/src/App.js.
This would require the React DOM to be available before Alert.alert can be called.

I'm not sure how we could handle this better
ezgif-5-b66429171338

@necolas Is it a requirement of the web version of the Alert.api to be available before React is mounted?

Before React is mounted? What does that mean?

In my implementation the would have to be included somewhere in the three, I don't know if that's possible in the AppRegistry.

So if you would could Alert.alert() before the <AlertRoot /> has rendered it would fail, I presume we should implement the Alert.alert outside of the component tree but that would be hard since <Modal> would not be available.

I think I can workaround the issue I described above, but is it possible to add extra components in the root of a react-native-web app, like an <AlertRoot /> or extra Context providers?

I now have rewritten the Alert proposal to add support for Alerts outside components.
https://codesandbox.io/s/alert-implementation-z4eoq?file=/src/Alert.js

It would still need something like ReactDOM.render(<AlertRoot />) when react-native-web registers the app

update: fixed a bug where multiple Alerts would open

Do you think the Codesandbox will be good enough for a PR if I

@RichardLindhout yeah something like that would be a good start

Do you think the Codesandbox will be good enough for a PR if I

I hate materialways design. It's like a plague, I think that at least, we should be able to modify it.

Hi, is there any update on this?
Would love to see a web version for the Alert.alert / .prompt in the same imperative approach.

And more user-friendly / good looking than plain old browsers' window.alert / window.prompt.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MovingGifts picture MovingGifts  路  3Comments

tgh picture tgh  路  3Comments

shirakaba picture shirakaba  路  3Comments

buffaloDeveloper picture buffaloDeveloper  路  3Comments

roryabraham picture roryabraham  路  3Comments