I cant quite figure out how I am supposed to pass an object as a prop when using useState in Next JS.
I have a lorem ipsum generator that I created in javascript functions. I have a component called Paragraphs
that houses it. I need to pass in two properties,
- a number of paragraphs.
- a sentence length.
The paragraph length is set by a text input where the user types in 1-10. The sentence length is set by radio buttons.
The problem I am running into is that when you input any value, the setState gets called (intentional) and it works, the problem is, it constantly works. I want to only have it update when I click my “Adventure” button to generate the data. I am unsure how to set those values to an set them as object property values and pass the object then.
Below is my code for the fields
import React, { useState } from 'react'
import Paragraph from '../components/ipsum/Paragraph.js'
export default function rpgIpsum() {
const [paragraphNumber, setParagraphNumber] = useState(5)
const [sentenceLength, setSentenceLength] = useState(5)
const [data, setData ] = useState({
outputProps: {
paragraphNumber: 5,
sentenceLength: 5
}
})
return (
<div>
{data.outputProps.paragraphNumber}
<div className="container">
<div className="row">
<div className="col-md-2 d-sm-none d-xs-none d-md-block d-none">
{/* <img src="public/images/Bard.jpg" alt="Lorem Ipsum Bard!" className="img-fluid" /> */}
</div>
<div className="col-md-10">
<h2>Looking to add some fun to your filler text?</h2>
<h5>Let's Spiffy up your copy with some RPG inspired Lorem Ipsum!</h5>
<div className="form-container">
<p>First, select how many paragraphs you want.
<input
type="text"
name="para"
value={paragraphNumber}
className="para-box"
required
onInput={
event => setParagraphNumber(parseInt(event.target.value))
}
/>
<small id="para-box-help" className="form-text text-muted">(Max of 10)</small>
</p>
<p>Next, select the length of the sentences</p>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="sentences"
value="3"
required
onInput={
event => setSentenceLength(parseInt(event.target.value))
}
/>
<label className="form-check-label" htmlFor="inlineRadio1">Short</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="sentences"
value="5"
required
onInput={
event => setSentenceLength(parseInt(event.target.value))
}
/>
<label className="form-check-label" htmlFor="inlineRadio2">Medium</label>
</div>
<div className="form-check form-check-inline">
<input
className="form-check-input"
type="radio"
name="sentences"
value="7"
required
onInput={
event => setSentenceLength(parseInt(event.target.value))
}
/>
<label className="form-check-label" htmlFor="inlineRadio3">Long</label>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary"
onClick={ event => "what do i do here?" ))}
>Adventure!</button>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-12">
<hr />
<Paragraph paragraphNumber={data.outputProps.paragraphNumber} sentenceLength={data.outputProps.sentenceLength}/>
</div>
</div>
</div>
</div>
)
}
Advertisement
Answer
What I’d do is refactor the input functionality into a separate component and use a function prop to pass the input data to an outer component that also contains the Paragraph
component, like so:
// rpgIpsum.js
export default function rpgIpsum() {
const [settings, setSettings] = useState({
paragraphNumber: 5,
sentenceLength: 5
});
return (
<>
<ParagraphInput onSubmit={setSettings} />
<Paragraph {settings} />
</>
);
}
// ParagraphInput.js
export default function ParagraphInput({ onSubmit }) {
const [paragraphNumber, setParagraphNumber] = useState(5);
const [sentenceLength, setSentenceLength] = useState(5);
return (
<div>
{/* ... */}
<button
type="submit"
onClick={() => onSubmit({paragraphNumber, sentenceLength})}
>Adventure!</button>
</div>
);
}
That way, settings
in rpgIpsum
is only updated when the button inside ParagraphInput
is pressed, and not on every change of the inputs.