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.