DEV Community

Sam Markham
Sam Markham

Posted on

Building a writing prompt generator with functional React

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!

Screenshot of prompt generator app

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.

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;

Enter fullscreen mode Exit fullscreen mode

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.

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;
Enter fullscreen mode Exit fullscreen mode

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:

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;

Enter fullscreen mode Exit fullscreen mode

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.

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;

Enter fullscreen mode Exit fullscreen mode

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.

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;

Enter fullscreen mode Exit fullscreen mode

As long as the generate() method was declared before useState was set up (because JavaScript doesn’t hoist function expressions), 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!

Top comments (0)