React Native local notifications with hooks and context
Tags: react, reactnative, tutorial, javascript
Published: 2019-09-20 08:42:07

In any application there are always times when you need to relay small pieces of information to the user. In web apps, this is usually done with a toast type message. In mobile apps, this is usually some sort of alert or local notification.

In this tutorial, we are gonna run through how to create reusable alert components that are updated with React context and hooks.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/i1c6g3l0xi7r2nzjzcje.gif)

We are gonna working with a simple notes app, you can clone the finished project from [here](https://github.com/jim-at-jibba/rn-local-notifications-tut).

You can see that it already has the functionality to view and create notes but we want to notify the user if saving the note was a success or if there was an error. Obviously this is just an example of where the alert can be used. It could be used to notify the user of anything!

In the example code, I am using the awesome [React Native Paper](https://reactnativepaper.com/). [Read my recent article](https://dev.to/salted-bytes/best-of-the-best-react-native-ui-libraries-31p3) to find out why I use it and why I think it the best React Native UI library. We are going to use the `Snackbar` component as our alert component but you could use anything that is appropriate for your project.

We are breaking the article into a few distinct parts.

We are going to use [`React.context`](https://reactjs.org/docs/context.html) for holding and setting our alert state. The React documentation states that

> Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Context is perfect for managing simple pieces of state that need to be available globally.

## Global Context Provider

I think it is good practice to split the state that is stored in context based on **domain**, i.e. having separate contexts for alerts, sidebar, cart etc. By doing this you can avoid unnecessary re-renders, your alert context is not fussed about your sidebar context and so updating one should not re-render components using another.

Context is made available to your application by wrapping your app in a `Context.provider`. But this can be a "problem" when you have multiple contexts as it makes your `App.js` bloated and slightly more unreadable. But all is not lost, Scott Tolinsky from LevelUp tuts put me onto a great snippet of code that composes your `Context.Providers` into a single component. This makes things so much neater. 

You have probably worked out that having multiple contexts in you `App.js` is not actually a problem. I just like to have things neat and tidy.

```javascript
import * as React from "react";
// we will import our context providers here

function ProviderComposer({ contexts, children }) {
  return contexts.reduceRight(
    (kids, parent) =>
      React.cloneElement(parent, {
        children: kids
      }),
    children
  );
}

function ContextProvider({ children }) {
  return (
    // we add our providers to the contexts prop
    <ProviderComposer contexts={[]}>{children}</ProviderComposer>
  );
}

export default ContextProvider;
```

## Alert provider

First, we need to create the context the can hold our alert state. We use `React.createContext` and assign it to a variable. Notice that we also export the variable, this means that we can use it later in our other components.

We also create an `AlertProvider` component that wraps our `AlertContext.Provider`, this is what gives us access to the state stored in our `AlertContext`.

```javascript
import * as React from "react";

export const AlertContext = React.createContext({});

export const AlertProvider = ({ children }) => {

  return (
    <AlertContext.Provider
      value={// Our context values will go here}>
      {children}
    </AlertContext.Provider>
  );
};
```

Next, we need a way to manage the data stored in our context. We could use `React.useState` for this but due to the slightly more complex structure of our data and the fact that we will be updating more than one piece of data to fire our alert component, I decided to use `React.useReducer` instead. It makes both the implementation of the Alert provider and the execution of its method so much neater.

```javascript
[...]

const initialState = {
  type: "close",
  open: false,
  alertType: "info",
  message: ""
};

const reducer = (state, action) => {
  switch (action.type) {
    case "close":
      return {
        ...initialState
      };
    case "open":
      return {
        open: true,
        alertType: action.alertType,
        message: action.message
      };
    default:
      throw new Error("Action not found");
  }
};

```

Finally, we need to put it all together and use our `reducer` in our `provider` giving us access to all the stored alert state. This combination allows us to update and access any part of the alert state from any part of the app, as long as the app is wrapped in our global context provider.

```javascript
import * as React from "react";

const initialState = {
  type: "close",
  open: false,
  alertType: "info",
  message: ""
};

export const AlertContext = React.createContext({});

const reducer = (state, action) => {
  switch (action.type) {
    case "close":
      return {
        ...initialState
      };
    case "open":
      return {
        open: true,
        alertType: action.alertType,
        message: action.message
      };
    default:
      throw new Error();
  }
};

export const AlertProvider = ({ children }) => {
  const [alertState, dispatchAlert] = React.useReducer(reducer, initialState);
  return (
    <AlertContext.Provider
      value={{
        alertState,
        dispatchAlert
      }}>
      {children}
    </AlertContext.Provider>
  );
};
```

## The Alert Component

As I mentioned at the start of this article we are using [React Native Paper](https://reactnativepaper.com/) and its Snackbar component to alert our users of any information in our apps. But this could be swapped out for anything else. You just need a way to consume the data that is being passed down from the alert context.

This component is quite simple. We are using the `React.useContext` hook to subscribe to changes to the `AlertContext` and then opening/closing the popup based on the state. We set the style of the alert box based on the `alertState.alertType` property to properly convey the meaning of the message.

```javascript
import * as React from "react";
import { Snackbar } from "react-native-paper";
import { AlertContext } from "../globalState";
import { colors } from "../constants";

const SnackBar = () => {
  const { alertState, dispatchAlert } = React.useContext(AlertContext);
  const [alertSyle, setAlertStyle] = React.useState({
    backgroundColor: colors.info
  });

  React.useEffect(() => {
    switch (alertState.alertType) {
      case "info":
        setAlertStyle({
          backgroundColor: colors.success
        });
        break;
      case "error":
        setAlertStyle({
          backgroundColor: colors.error
        });
        break;
      case "success":
        setAlertStyle({
          backgroundColor: colors.success
        });
        break;
      default:
        setAlertStyle({
          backgroundColor: colors.info
        });
    }
  }, [alertState]);

  const closeMe = () => {
    dispatchAlert({ type: "close" });
  };

  return (
    <>
      {typeof alertState.open === "boolean" && (
        <Snackbar
          style={alertSyle}
          visible={alertState.open}
          onDismiss={() => closeMe()}
          action={{
            label: "Undo",
            onPress: () => {
              console.log("Snackbar closed");
              // Do something
            }
          }}>
          {alertState.message}
        </Snackbar>
      )}
    </>
  );
};

export default SnackBar;
```

## Using our Alert provider

Finally, we are now in a position to use our sweet new `AlertContext` from anywhere in our app. In the example below, we are notifying the user of the outcome of their GraphQL mutation.

If you want to learn how to easily integrate GraphQL into your React Native Application take a look at [this article](https://www.gravitywell.co.uk/insights/react-apollo-and-react-hooks-a-match-made-in-heaven/).

In the same way, we did in our Snackbar component, we are using the `useContext` hook to gain access to the `dispatchAlert` method which will allow us to alert the user to the success or errors in there GraphQL mutation.

```javascript
[...]

const NoteCreateScreen = ({ navigation }) => {
  const { dispatchAlert } = React.useContext(AlertContext);

  const createNoteMutation = useMutation(gql(createNote));
  return (
    <SafeAreaView style={gStyle.container}>
      <ScrollView contentContainerStyle={gStyle.contentContainer}>
        <View style={{ flex: 1, height: '100%', width: '100%' }}>
          <Surface style={styles.surface}>
            <Formik
              initialValues={{ note: '', title: '' }}
              onSubmit={({ note, title }) => {
                const input = {
                  id: uuid(),
                  title,
                  note,
                  createdAt: moment().toISOString()
                };
                createNoteMutation({
                  variables: {
                    input
                  },
                  update: (_, { data, error }) => {
                    if (error) {
                      dispatchAlert({
                        type: 'open',
                        alertType: 'error',
                        message: 'Error creating note'
                      });
                    } else {
                      dispatchAlert({
                        type: 'open',
                        alertType: 'success',
                        message: 'Note created'
                      });
                      navigation.state.params.refetch();
                      navigation.goBack();
                    }
                  }
                });
              }}
            >
              {({ values, handleSubmit, handleChange }) => {
                return (
                  <>
                    [...]
                  </>
                );
              }}
            </Formik>
          </Surface>
        </View>
      </ScrollView>
    </SafeAreaView>
  );
};

export default NoteCreateScreen;
```

And there you have it, a highly customisable, reusable and globally executable local notification system. This type of situation is a perfect use for `React Context` but obviously there are so many more.

What other stuff will you create?

# Thanks for reading 🙏

If there is anything I have missed, or if there is a better way to do something then please let me know