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!
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;
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;
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;
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;
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;
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)