React | Build a Slack Contact Form
Tags: react, webdev, beginners, javascript
Published: 2020-12-04 02:14:11


# Intro

Contact forms can be annoying if you have to deal with an email provider, server-side logic, and all that. So this is a quick and easy way you can work around it by taking advantage of a free Slack app called [Incoming-Webhooks](https://slack.com/apps/A0F7XDUAZ-incoming-webhooks). You can Post directly to the channel using only client-side technologies with a simple Javascript fetch. Neat right?

# Technologies and concepts covered

Familiarity with React is recommended for this tutorial. However it is fairly basic, so if you're a beginner with React then this tutorial is for you. This tutorial will be using the following technologies and concepts:
- Implementing an App in a Slack Workspace
- Next.js
- React hooks
  - useState
- Functional components in React
- ES6 syntax
  - Promises using the ".then -> .catch" syntax

# Prerequisite

You will need to have a Slack workspace (that you have sufficient priveledges in) to add the Incoming-Webhooks app for this tutorial. To create a new workspace, head on over to [slack.com](https://slack.com/get-started#/create). 

To add the Incoming-Webhooks app, search the app marketplace. Open up your new or existing workspace, and use the three-dot "more" dropdown in the sidebar to add apps.

![Screen Shot 2020-12-03 at 5.03.37 PM](https://dev-to-uploads.s3.amazonaws.com/i/sncitxm3o7pjki8gkkc9.png)

Browse the App Directory for Incoming-Webhooks. Once you've added it to your workspace, head to the "Configuration" tab. The important item here for connecting our application is the URL to which we will be POSTing our Fetch. Under "Edit Configuration", scroll down until you find this URL. It should look like this:

![Screen Shot 2020-12-03 at 5.55.37 PM](https://dev-to-uploads.s3.amazonaws.com/i/15qc8war0xmb1icy0w84.png)
 
 
# Project setup

I did this with my existing portfolio website project. You might as well too if you are looking for a simple way to add a contact form to your React app portfolio. But if you'd like to kick your project off from scratch with the technology I used and follow along, you'll need to use Next.js. So go ahead and start a basic project with that. Their quickstart docs can be found here [Next.js](https://nextjs.org/docs/getting-started). You will need to run the following in the directory of your choosing for a new project:

```bash
npx create-next-app
# or
yarn create next-app
```

Next.js comes with a bunch of cool features out of the box, so you don't need much setup to get rolling. I like to build my apps with a pages/Components type of structure, where my page layout logic is in the pages, and then re-usable components are in the Components folder. I've also got a utils folder for my fetch logic. The important files for this tutorial end up looking like this structure:

```text
── src
│   ├── Components
│   │   ├── SlackContact
│   │   │   ├── SlackContact.js
│   ├── pages
│   │   ├── index.js
│   └── utils
        ├── fetchSlack.js
```
Go ahead and create those files now, or refer back to this tree and create the files as you go. Whatever work flow is most comfortable for you.

# Build the page

Let's use functional components to start and build our page layout. Our `index.js` file is where the page will live, and where we will be able to control the layout of our components. In this case, we only will have the one component, `SlackContact.js`. But moving forward you may want to add other items to your page like a nav bar, footer, etc.

Next.js looks in the pages directory for it's page routing, so this is set up out of the box and really straight forward. If you wanted a new page at the path `/whatever`, all you need to do is create a new file in the pages directory called `whatever.js`. 


```react
// index.js
import React from 'react';
import Head from 'next/head'

export default function Home() {
  return (
    <>
      <Head>
        <title>Tateronaut | Home</title>
      </Head>

      <main>
        <p>stuff will go here!</p>
      </main>
    </>
  )
}
``` 

Simple enough. You can see we're using some Next.js functionality with their built in `next/head` component, which lets us add things like the page title, or page links in the Head part of the DOM.

## Initialize the component

Alright, head on over to the `SlackContact.js` file to start our component. Very similar to the above, let's get a basic functional component going using ES6 arrow function syntax for funzies. You'll notice I'm using another library, ["nes-react"](https://github.com/bschulte/nes-react), which is a super fun retro video game style library that I really like.

```react
// SlackContact.js
import React, { useState } from 'react';
import { Container } from 'nes-react';

export const SlackContact = () => {

  return (
    <Container title='Contact My Slackbot'>
      <h1>Contact Me!</h1>
    </Container>
  );
};

export default SlackContact;
```

Sweet, now we can import our component in our page, and we will be done with the `index.js` file.

```react
// index.js
import React from 'react';
import Head from 'next/head';
import SlackContact from '../../Components/SlackContact/SlackContact';

export default function Home() {
  return (
    <>
      <Head>
        <title>Tateronaut | Home</title>
      </Head>

      <main>
        <SlackContact />
      </main>
    </>
  )
}
```

# Fetch logic

Now we are going to work on the logic for Posting to our slack channel. Create a file in your `utils` folder called `fetchSlack.js`. This is basically straight from [MDN's documentation](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) on using fetch, but adapted for our purposes.

```javascript
// fetchSlack.js
export const fetchSlack = async (url='', data={}) => {
  const res = await fetch(url, {
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'no-cors', // no-cors, *cors, same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'include', // include, *same-origin, omit
    headers: {
      'Content-Type': 'application/json'
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    redirect: 'follow', // manual, *follow, error
    referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: JSON.stringify(data) // body data type must match "Content-Type" header
  });
  return res;
}
export default fetchSlack;
```

You could just add this straight into your `SlackContact.js` component. But that would make the component messier and more difficult to unit test. Keeping our fetch logic outside the component in the `/utils` folder is a more readable approach.

# Build the contact form

Alright, now we're going to build the UI for the form. Switch back to your `SlackContact.js` and add a text area, and two text input fields. I'm using the ones that came with `nes-react`, but you can use whatever components you'd like for these. 

We will also be adding in our `useState` hook, and setting up our local state for the input fields.

```react
// SlackContact.js
import React, { useState } from 'react';

import { Container, TextInput, TextArea, Button } from "nes-react";

export const SlackContact = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');

  return (
    <Container title="Contact My Slackbot" className='slack-contact'>
          <TextInput
            label="Name"
            placeholder="Input Name"
            value={name}
            onChange={e => {setName(e.target.value)}}
          />
          <TextInput
            label="Email"
            placeholder="Input Email"
            value={email}
            onChange={e => {setEmail(e.target.value)}}
          />
          <TextArea
            label="Message"
            placeholder="Start Writing"
            value={message}
            onChange={e => {setMessage(e.target.value)}}
          />
          <Button onClick={handleClick} success>SEND IT!</Button>
    </Container>
  );
};

export default SlackContact
;
```

That is a working contact form, that you could use to send the information stored in your local state anywhere. At this point though it is just a pretty UI, that doesn't do anything when you use the submit button. Note that `handleClick` hasn't been defined. Let's add that in now. This is where we will bring in our our `fetchSlack` function that we wrote in our `/utils` folder. We also need to define and add in the URL that we got from the Incoming-Webhooks app configuration in the Prerequisites section of this tutorial. You may want to store this in your `.env` file for security purposes, so that other people can't post to your channel by getting your URL off your code repository if it is public. But that is outside the scope of this project. The updated `SlackContact.js` file will look like this:

```react
// SlackContact.js
import React, { useState } from 'react';

import { Container, TextInput, TextArea, Button } from "nes-react";

/** helpers */
import fetchSlack from '../../utils/fetchSlack';

export const SlackContact = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');

  const webhookURL = '<YOUR WEBHOOK URL>';
  const myData = {
    "text": `Name: ${name} \n${email} \n${message}`
  }

  const handleClick = () => {
    fetchSlack(webhookURL, myData)
      .then(response => console.log(response))
      .then(data=>{
        return data;
      })
      .catch(err => {
        console.error('There was an ERROR!!!',err)
      });
  }


  return (
    <Container title="Contact My Slackbot" className='slack-contact'>
          <TextInput
            label="Name"
            placeholder="Input Name"
            value={name}
            onChange={e => {setName(e.target.value)}}
          />
          <TextInput
            label="Email"
            placeholder="Input Email"
            value={email}
            onChange={e => {setEmail(e.target.value)}}
          />
          <TextArea
            label="Message"
            placeholder="Start Writing"
            value={message}
            onChange={e => {setMessage(e.target.value)}}
          />
          <Button onClick={handleClick} success>SEND IT!</Button>
    </Container>
  );
};

export default SlackContact
;
```

# Conclusion

And that should be all you need to get started posting to a slack channel. I hope it was clear enough to follow along. If I missed anything, or it doesn't seem obvious, let me know in the comments. 

For next steps, you might want to add some basic form validation. We can do that by improving the `onChange` functions of each of our text inputs. Some basic things like checking that the email matches simple email requirements would be nice. Additionally, it might be helpful to move the useState items out of local state, and use a state management tool like [Redux](https://redux.js.org/introduction/getting-started) so that these values are accessible to other components in our application. 

I hope you enjoyed this article!