Codementor Events

A Better way To Show Toasts and Navigate in React Native

Published May 05, 2019

It's a very common feature to show toasts in an app to inform the user of certain events. For eg showing a ' you have gone offline ' toast on loss of internet connectivity.
An example on how to show a toast taken directly from the docs of react-native-easy-toast is as follows

render() {
       return (
           <View style={styles.container}>
               <TouchableHighlight
                   style={{padding: 10}}
                   onPress={()=>{
                       this.refs.toast.show('hello world!');
                   }}>
                   <Text>Press me</Text>
               </TouchableHighlight>
               <Toast ref="toast"/>
           </View>
       )
   }

But what if we
Don't want to use a Toast component for each component
Don't want to make refs for each Toast component
Want to show a toast from redux-thunk or redux-saga itself after a certain side-effect

These problems can be solved by adding a single Toast component to the app's Root component .
If you are using redux an example would be as follows

class Root extends React.Component {
  ...
    render() {
        return (
            <Provider>
                <View>
                    <ApplicationContainer />
                    <Toast ref="toast" />
                </View>
            </Provider>
        );
    }
}

But how to make the toast available to components that require it ??????
We can
Pass the ref to all components via react context API
Store the ref in a variable and make it accessible to all our components via export

Is there a better way to do it ?? There has to be, right ??
Enter - Node Style Event Emitters To The Rescue
There are many event emitters out there which can be used to emit, listen and take actions on events , for egMitt (a Tiny 200b functional event emitter / pub-sub .)
Using Event Emitters for Showing Toasts
The above problems can be solved if associate an event to the toast and keep emitting the toast event whenever we need to show a toast
Now the above code can be modified as

const emitter =  mitt()
class Root extends React.Component {
 ...
   showToast = params => {
       if (this.toast.current) {
           this.toast.current.show(params.message, params.duration);
       }
   };
   componentDidMount() {
       emitter.on("showToast", this.showToast);
   }
   componentWillUnmount() {
       emitter.off("showToast", this.showToast);
   }
   render() {
       return (
           <Provider>
               <View>
                   <ApplicationContainer />
                   <Toast ref="toast" />
               </View>
           </Provider>
       );
   }
}

// Now from any component if we want to show a toast

class SubscribingComponent {
   render() {
       return (
           <View>
               <TouchableHighlight
                   onPress={() => {
                       emitter.emit("showToast", {
                           message: "Hello World",
                           duration: 1000
                       });
                   }}
               >
                   <Text>Press me</Text>
               </TouchableHighlight>
           </View>
       );
   }
}

This seems a much better, concise and pretty way to handle toasts.
The only disadvantage that I foresee is we can't style the toast for particular scenarios. If styling is not an issue for you , then maybe you can try out showing toasts using event emitters
Using Event Emitters for Navigation
Event Emitters can again be used particularly in react native in certain scenarios when we have to navigate to some other screen on the completion of a side-effect(actions). Even though react-navigation provides a way to access navigation outside your component, but navigating from outside the component specially from redux-thunk or redux-saga feels a bit weird . A small example would be as follows
// in your thunk or saga

function* someSaga() {
   // this saga responds to the action
   // api call
   yield call(apiCall);
   // on success emit navigate event
   emitter.emit("navigateEvent", {
       routeName: "route",
       params: { title: "hello" }
   });
}
class SampleComponent {
   componentDidMount() {
       emitter.on("navigateEvent", this.redirectToSomePage);
   }

   componentWillUnmount() {
       emitter.off("navigateEvent",this.redirectToSomePage);
   }

   redirectToSomePage = route => {
       navigation.navigate(route.routeName, route.params);
   };

   render() {
       return (
           <TouchableOpacity onPress={DispatchSomeAction}>
               <Text> Press me</Text>
           </TouchableOpacity>
       );
   }
}

Hope this helps … cheers !!!!

Discover and read more posts from VINEETH GEORGE
get started
post commentsBe the first to share your opinion
Show more replies