简体   繁体   中英

React Native: custom component that is accessible from every app screen

What is the best 'React' way of creating a custom component that can be accessible from any screen?

Think of a custom Alert component . I need it in every screen of my app, so when an error occurs I can show it.

Currently I am doing it this way:

// AlertModal.js

import ... from ...;

const AlertModal = (props) => {

  const {isSuccess, headerText, bodyText, onClosed, isOpen, onBtnPress} = props;

  return (

      <Modal
        ...
        isOpen={isOpen}
        onClosed={() => onClosed()}
      >

        <View ...>

            <Text>{headerText}</Text>

          <Text>{bodyText}</Text>

          <Button
            ...
            onPress={() => onBtnPress()}
           >
              ...
           </Button>

        </View>

      </Modal>

  )
};

export default AlertModal;

//Foo.js

import ...from ...;

const Foo = (props) => {

  const { alertIsSuccess, alertHeaderText, alertBodyText, alertIsOpen, alertOnClosed, alertOnBtnPress, onBtnPress, ...rest } = props;

  return (
    <View style={}>

     ...     

      <View style={}>
        ...
      </View>


        <Button
          onPress={() => onBtnPress()}
        />

      <AlertModal
        isSuccess={alertIsSuccess}
        headerText={alertHeaderText}
        bodyText={alertBodyText}
        isOpen={alertIsOpen}
        onClosed={alertOnClosed}
        onBtnPress={alertOnBtnPress}
      />

    </View>
  )

};

export default QrCodeReader;

//FooContainer.js

import ... from ...;

class FooContainer extends Component {
    constructor(props) {
        super(props);

        this.state = {
          bar: false,
          baz: true,
          bac: '',
          bax: false,
          bav: '',

          // Alert
          alertIsOpen: false,
          alertIsSuccess: false,
          alertHeaderText: '',
          alertBodyText: '',
          alertOnClosed() {},
          alertOnBtnPress() {},
        };

      this.onBtnPress = this.onBtnPress.bind(this);
    }

    alert(isSuccess, headerText, bodyText, onBtnPress, onClosed) {

      const self = this;

      this.setState({
        alertIsOpen: true,
        alertIsSuccess: isSuccess,
        alertHeaderText: headerText,
        alertBodyText: bodyText,
        alertOnBtnPress: onBtnPress || function () { self.alertClose() },
        alertOnClosed: onClosed || function () {},
      });

    }

    alertClose() {

      this.setState({
        alertIsOpen: false,
      });

    }

    onBtnPress() {

        this.alert(
          true,
          'header text',
          'body text',
        )

    }

    render() {
        return (
          <Foo
            onBtnPress={this.onBtnPress}
            {...this.state}
          />
        );
    }
}

export default FooContainer;

As you can see it is a pain (and I think an incorrect way). Doing this way I would need to include AlertModal component in every component of my app where I need to display alerts = duplicate props and making new unnecessary <AlertModal /> components.

What is the correct way?

ps I use react-native-router-flux as a router in my app. pss I am coming to React-native from a Meteor.js + Cordova . There I can just create one modal and include it in the main layout and show/hide it when necessary with the appropriate dynamic text inside it.

This is how I navigate in my app:

//Main.js
class Main extends Component {
      render() {
        return (
          <Router backAndroidHandler={() => true}>
            <Scene key="root">
              <Scene key="login" type='reset' component={SignInContainer} initial={true} hideNavBar={true}/>
              <Scene key="qrCode" type='reset' component={FooContainer} hideNavBar={true} />
            </Scene>
          </Router>
        );
      }
}

Based on the fact that you use react-native-router-flux I can suggest:

Rework AlertModal into scene component on is own, do not use Modal. You can control what to display in it by passing properties. Include AlertModal component in your router as 'modal' schema like this:

import AlertModal from '../components/AlertModal';
//Main.js
class Main extends Component {
  render() {
    return (
      <Router backAndroidHandler={() => true}>
        <Scene key="root">
          <Scene key="login" type='reset' component={SignInContainer} initial={true} hideNavBar={true}/>
          <Scene key="qrCode" type='reset' component={FooContainer} hideNavBar={true} />
          <Scene key="alertModal" duration={250} component={AlertModal} schema="modal" direction="vertical" hideNavBar={true} />
        </Scene>
      </Router>
    );
  }
}

And then you can just call it from any scene with:

Actions.alertModal({ >optional props you want to pass< });

Not sure if is desired behaviour but this kind of modals may be dismissed in Android with back button. If not, there is a way around it.

This might not be the best solution, but what you can do is use the context functionality of React. Essentially, pass a function reference that triggers your component to show/hide (maybe by changing its state , for instance) into the context. Then every child in the hierarchy should be able to call that function.

However, Facebook discourages using the context functionality, and it might give you some problems when debugging, since it is not as easy to track as your props/state.

Another solution that comes to mind might be playing with Redux, creating an action that changes a property to which the component is subscribed. Then all your other components can just dispatch that action, changing the value of the property that makes the modal component to display.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM