Building a writing prompt generator with functional React
Tags: react, codenewbie
Published: 2020-09-09 02:13:42

As well as a dev, I’m a fiction writer, and sometimes I run out of ideas for what to write. A couple of weeks ago this gave me an idea for what to code—a [writing prompt generator](https://promptgenerator.netlify.app/)! 

![Screenshot of prompt generator app](https://dev-to-uploads.s3.amazonaws.com/i/8ww354qlpdypw5ohs7s2.png)

There are a lot of these out there, of course, but making one myself was fun, and as my previous React app was made with mostly class components, for this project I took the opportunity to get more familiar with function components in React along the way.

This post will walk through how I set this up! It assumes some familiarity with React as a library.

## 1. Multiple generator pages 

The first thing I wanted to accomplish with this generator was allowing the user to generate prompts from multiple sets of words, with different pages for each set of prompts. To this aim, I created two prompt page components and one generator component that could be rendered with a different set of props in each of them. 

```javascript
import React from 'react';
import Generator from './Generator';
import {words} from '../assets/words';

const WordsPromptPage = () => {
  return (
    <div>
      <p>Write 250 words about:</p>
      <Generator dataset={words}/>
    </div>
  );
}

export default WordsPromptPage;

```

Here the WordsPromptPage component is rendering a Generator component with a dataset of `words`, which is an array in a separate file. I could have made a database for different types of prompts, but I decided to keep them in asset files on the frontend instead to be able to host the generator more simply as a frontend on Netlify instead of hosting a backend and frontend separately. 

## 2. Navigation

To switch between different generator pages (and my about page) in the app, I used `react-router-dom` in the main `App` component so that when the URL changes, the component shown changes too. 

```javascript
import React from 'react';
import './App.css';
import WordsPromptPage from './components/WordsPromptPage';
import NauticalWordsPromptPage from './components/NauticalWordsPromptPage';
import {
  Switch,
  Route
} from 'react-router-dom';
import NavBar from './components/NavBar';
import AboutPage from './components/AboutPage';

function App() {
  return (
    <div className="App">
      <NavBar/>
      <h1>Prompt Generator</h1>
      <Switch>
        <Route exact path="/nauticalwords"><NauticalWordsPromptPage/></Route>
        <Route exact path="/about"><AboutPage/></Route>
        <Route path="/"><WordsPromptPage/></Route>
      </Switch>
    </div>
  );
}

export default App;
```

I used `Switch` here because I want only one matched `Route` to render and `Switch` renders a route exclusively—I don’t want users to be able to type in `/nauticalwords` and have `WordsPromptPage` and `NauticalWordsPromptPage` both render on the same page because `/nauticalwords` matches both `/nauticalwords` and `/`. 

`Switch` is below the heading and the `NavBar` component so that while part of the page will change according to the URL, the heading and navigation will always render. 

In the `NavBar` component, I put the links to these different routes: 

```javascript
import React from 'react';
import { Link } from 'react-router-dom';

const NavBar = () => {

  return (
    <nav role="navigation">
        <Link className="nav-link" to="/">Words</Link>
        <Link className="nav-link" to="/nauticalwords">Nautical Words</Link>
        <Link className="nav-link" to="/about">About</Link>
    </nav>
  );
}

export default NavBar;

```

## 3. Function components	

The last app I built with React I used mostly class components—the function components I did use were simple presentational components: components mostly concerned with how things look, often (and in my case) written as stateless function components. When I needed to deal with state, I stuck with class components. 

For this project, I wanted to start getting more familiar with function components, specifically using hooks to be able to deal with state in function components. 

According to the React documentation, hooks are “functions that let you 'hook into' React state and lifecycle features from function components… they let you use React without classes.”

For my generator component, I used useState to return a `prompt` variable and also a `setPrompt` function to let me update the prompt. 

```javascript
import React, { useState } from 'react';

const Generator = (props) => {

  const [prompt, setPrompt] = useState(“Click to generate a prompt!”)

  const generate = () => {
    return props.dataset[Math.floor(Math.random() * props.dataset.length)];
  }


  return (
    <div>
      <p id="prompt">{prompt}</p>
      <button onClick={() => setPrompt(generate())}>Generate</button>
    </div>
  );
}

export default Generator;

```

The initial state here was set to the string, “Click to generate a prompt!”, and on button click, `prompt` is set to the return value of the `generate()` function. 

After getting this set up though, I realized it would be a better idea to have the site load with a random prompt generated as well, and I could probably use `useState` to do this too. 

```javascript
import React, { useState } from 'react';

const Generator = (props) => {

  const generate = () => {
    return props.dataset[Math.floor(Math.random() * props.dataset.length)];
  }

  const [prompt, setPrompt] = useState(() => generate())


  return (
    <div>
      <p id="prompt">{prompt}</p>
      <button onClick={() => setPrompt(generate())}>Generate</button>
    </div>
  );
}

export default Generator;

```

As long as the `generate()` method was declared before `useState` was set up (because JavaScript [doesn’t hoist function expressions](https://blog.bitsrc.io/hoisting-in-modern-javascript-let-const-and-var-b290405adfda)), I could set the initial state for `prompt` to the return value of `generate()` as well, so that the app would have a newly generate prompt on loading and on refresh. 


## Conclusion 

I had a lot of fun building this app, and it was a good way to spend a few days familiarizing myself with function components, as well as spending more time with React after a break away from it! I look forward to diving deeper into functional React in the future—and expanding the types of writing prompts in the generator!