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>
)
}
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.
You need to make a parent component and lift the state of your components up to that parent component. Or you can use Redux for your state management, it would make it easier for you to pass data to your components.
Related
I have been trying to display the form data submitted but the map is throwing an error.
I have two components
NameForm.js
Here is the form input, handlechange and handlesubmit methods are done
function Nameform() {
const [form, setForm] = useState({firstname: "", lastname: ""});
const handleChange = (e) => {
setForm({
...form,
[e.target.id]: (e.target.value),
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("hello from handle submit", form );
}
return (
<section>
<div className='card pa-30'>
<form onSubmit={ handleSubmit }>
<div className='layout-column mb-15'>
<label htmlFor='name' className='mb-3'>First Name</label>
<input
type='text'
id='firstname'
placeholder='Enter Your First Name'
data-testid='nameInput'
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className='layout-column mb-15'>
<label htmlFor='name' className='mb-3'>First Name</label>
<input
type='text'
id='firstname'
placeholder='Enter Your First Name'
data-testid='nameInput'
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className='layout-row justify-content-end'>
<button
type='submit'
className='mx-0'
data-testid='addButton'
>
Add Name
</button>
</div>
</form>
</div>
</section>
)
}
export default Nameform
NameList.js
I want to pass the data in handleSubmit in NameForm.js to NameList.js. But the data is not displayed.
function NameList({form}) {
return (
<section>
{form.map(displayName => {
return (
<ul
className='styled w-100 pl-0'
>
<li
className='flex slide-up-fade-in justify-content-between'
>
<div className='layout-column w-40'>
<h3 className='my-3'>{displayName.firstname}</h3>
<p className='my-0'{displayName.lastname}></p>
</div>
</li>
</ul>
)
})}
</section>
)
}
export default NameList;
App.js
In App.js, I want to display both the form and the data.
import { Nameform, Namelist } from './components'
function App() {
return (
<div>
<div className='layout-row justify-content-center mt-100'>
<div className='w-30 mr-75'>
<Nameform />
</div>
<div className='layout-column w-30'>
<NameList />
</div>
</div>
</div>
)
}
export default App;
Thank you for your help!
Pass the data you want to share between parent and children via props (which stands for properties).
In the parent class, when rendering <NameForm> and <ListForm> add the data like that:
//if you want to share count and name for example:
<NameForm
count={this.state.count}
name={this.state.name}
/>
You can add as many props as you want. Furthermore, you can pass a function and its argument using arrow functions:
<NameForm
aFunction={() => this.myFunction( /* anArgument */ )}
/>
To access props in a child class dynamically wherever you need them:
{this.props.count}
{this.props.name}
{this.props.aFucntion}
You can get rid of this.props using a technique called object destructing:
render(
const {count, name, aFunction} = this.props;
//now you can use {count} and {name} and {aFunction} without this.props
);
There are some bugs in your code, first form is an object not an array, so you can't map it, you need to use form.firstname and form.lastname, Also you set both input ids equal firstname you need to modify it, Also you need to move the form state and handleChange function to the App component.
This is a working code of your example.
https://codesandbox.io/s/upbeat-forest-328bon
You can save the state in the parent component and pass it as props to the child components like so.
Here we make use of an outer state called submittedForm to display only the submitted values. The inner form state is being used for handling the values before submitting.
// App.js
function App() {
const [submittedForm, setSubmittedForm] = useState({
firstname: "",
lastname: "",
});
return (
<div>
<div className="layout-row justify-content-center mt-100">
<div className="w-30 mr-75">
<NameForm setSubmittedForm={setSubmittedForm} />
</div>
<div className="layout-column w-30">
<NameList form={submittedForm} />
</div>
</div>
</div>
);
}
export default App;
// NameForm.js
function NameForm({ setSubmittedForm }) {
const [form, setForm] = useState({
firstname: "",
lastname: "",
});
const handleChange = (e) => {
// setActive(true);
setForm({
...form,
[e.target.id]: e.target.value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
setSubmittedForm(form);
};
return (
<section>
<div className="card pa-30">
<form onSubmit={handleSubmit}>
<div className="layout-column mb-15">
<label htmlFor="name" className="mb-3">
First Name
</label>
<input
type="text"
id="firstname"
placeholder="Enter Your First Name"
data-testid="nameInput"
value={form.firstname}
onChange={handleChange}
/>
</div>
<div className="layout-column mb-15">
<label htmlFor="name" className="mb-3">
Last Name
</label>
<input
type="text"
id="lastname"
placeholder="Enter Your Last Name"
data-testid="nameInput"
value={form.lastname}
onChange={handleChange}
/>
</div>
<div className="layout-row justify-content-end">
<button type="submit" className="mx-0" data-testid="addButton">
Add Name
</button>
</div>
</form>
</div>
</section>
);
}
export default NameForm;
// NameList.js
function NameList({ form }) {
return (
<section>
<ul className="styled w-100 pl-0">
<li className="flex slide-up-fade-in justify-content-between">
<div className="layout-column w-40">
<h3 className="my-3">{form.firstname}</h3>
<p className="my-0">{form.lastname}</p>
</div>
</li>
</ul>
</section>
);
}
export default NameList;
I am new to react and I got a scenario where I have multiple checkboxes on my form. I want the user to check all the checkboxes only then enable the submit button on the form. On Load, the submit button will be disabled. How to do this?
Here is the code that I have written so far:
const MultipleCheckboxes = () => {
const handleChange = () => {
}
const allChecked = () => {
}
return (
<div>
<form>
<div className="form-check">
<input
type="checkbox"
className="some-class-name-chk"
name="someName"
value="Java"
id="languageChkDefault"
onChange={handleChange}
/>
<label
className="form-check-label"
htmlFor="languageChkDefault"
>
Javascript
</label>
</div>
<div className="form-check">
<input
type="checkbox"
className="some-class-name-chk"
name="someName"
value="Angular"
id="languageChkDefault"
onChange={handleChange}
/>
<label
className="form-check-label"
htmlFor="languageChkDefault"
>
Angular
</label>
</div>
<div className="form-check">
<input
type="checkbox"
className="some-class-name-chk"
name="someName"
value="Python"
id="languageChkDefault"
onChange={handleChange}
/>
<label
className="form-check-label"
htmlFor="languageChkDefault"
>
Python
</label>
</div> <br />
<button disabled={!allChecked}>Submit</button>
</form>
</div>
);
};
export default MultipleCheckboxes;
Can anybody help me on this? thank you
This is my solution, I hope it helps you
import { useState, useEffect } from "react";
const MultipleCheckboxes = () => {
const [allChecked, setAllChecked] = useState(false);
const [checkboxes, setCheckboxes] = useState({
javaCheckbox: false,
angularCheckbox: false,
pythonCheckbox: false
});
function handleChange(e) {
setCheckboxes({
...checkboxes,
[e.target.id]: e.target.checked
})
}
useEffect(() => {
const result = Object.values(checkboxes).every(v => v);
console.log(result);
setAllChecked(result);
}, [checkboxes]);
return (
<div>
<form>
<div className="form-check">
<input
type="checkbox"
className="some-class-name-chk"
name="someName"
value={checkboxes.javaCheckbox}
id="javaCheckbox"
onChange={handleChange}
/>
<label
className="form-check-label"
htmlFor="languageChkDefault"
>
Javascript
</label>
</div>
<div className="form-check">
<input
type="checkbox"
className="some-class-name-chk"
name="someName"
value={checkboxes.angularCheckbox}
id="angularCheckbox"
onChange={handleChange}
/>
<label
className="form-check-label"
htmlFor="languageChkDefault"
>
Angular
</label>
</div>
<div className="form-check">
<input
type="checkbox"
className="some-class-name-chk"
name="someName"
value={checkboxes.pythonCheckbox}
id="pythonCheckbox"
onChange={handleChange}
/>
<label
className="form-check-label"
htmlFor="languageChkDefault"
>
Python
</label>
</div> <br />
<button disabled={!allChecked}>Submit</button>
</form>
</div>
);
};
export default MultipleCheckboxes;
You can use react useState hook and set its default value to false.
const [state, setstate] = useState(false)
When the user clicks on an input box set the state to true. You may encounter some problems when the user unchecks the box, so you can use an if statement to handle state change
For example:
if (state === true){
setstate(false)
}else{
setstate(true)
}
or you can just use this code inside the handleChange function:
!state
It inverses the state to true/false accordingly.
After that you can use a ternary operator inside the component to check whether the user has checked all the boxes, if the user hasn't checked all the boxes, disable the button or else do otherwise.
For example, if the state is true (or in other words, if the user has checked the box), render the normal styled button, else render the disabled button:
{state? <button className = "styled"/>: <button disabled className="styled"/>}
Of course, I have only checked the state of one input box. Now you can simply check for multiple conditions by declaring multiple states for each box.
{state1 && state2 && state3 ? <button className = "styled"/>: <button disabled className="styled"/>}
If you are not yet familiar with ternary operators, you should go through this doc Ternary operators.
If you haven't heard of useState and react hooks yet, feel free to look the React's documentation. Welcome to the React ecosystem!
I am working on using modals to accept form inputs on a react project
here is the plan component
plan/index.js
import React, { useState } from "react";
import Pay from '../Pay';
const Plan = () => {
const [payModal, setPayModal] = useState(false);
const [planMode, setPlanMode] = useState(true);
return (
<main class="main">
{payModal && <Pay setOpenPayModal={setPayModal} />}
<div class="plan">
<div>
<a class="plans" onClick={() => {setPayModal(true);}}>plan A</a>
<div class="plan">
<span class="plan">{!planMode ? "$19.99" : "$9.99"}</span>
<span class="plan">{!planMode ? "month" : "year"}</span>
</div>
</div>
<div>
<a class="plans" onClick={() => {setPayModal(true);}}>plan B</a>
<div class="plan">
<span class="plan">{!planMode ? "$29.99" : "$19.99"}</span>
<span class="plan">{!planMode ? "month" : "year"}</span>
</div>
</div>
</div>
</main>
);
};
export default Plan;
as you can see on the line {payModal && <Pay setOpenPayModal={setPayModal} />} where i call the pay modal component from the plan component and it opens up the modal
here is the pay component which is the modal component
pay/index.js
import React, { useState } from "react";
function Pay({ setOpenPayModal }) {
const [billingDetails, setBillingDetails] = useState({
price: "",
: "",
});
return (
<div class="modal">
<div class="modal">
<div class="close">
<button
onClick={() => {
setOpenPayModal(false);
}}
>
</button>
</div>
<div class="modal">
<form class="form" onSubmit={handleSubmit}>
<fieldset class="form">
<Field
label="Price"
id="price"
type="text"
value={billingDetails.price}
onChange={(e) => {
setBillingDetails({ ...billingDetails, price: e.target.value });
}}
/>
<Field
label="Frequency"
id="frequency"
type="text"
value={billingDetails.frequency}
onChange={(e) => {
setBillingDetails({ ...billingDetails, frequency: e.target.value });
}}
/>
</fieldset>
<button class="pay" onClick={handleSubmitPay}>
Pay
</button>
</form>
</div>
</div>
</div>
);
}
export default Pay;
The issue is I want to be able to pass the values price and frequency from the plan component to the pay modal component
for example for plan A is price="$19.99" and frequency="year", so based on the button clicked on the plan component page, those values get passed to the pay modal component in a dynamic way
how do I achieve this using react hooks?
You can use contexts to pass, but in this case I don't think it's the best option. What I really recommend is passing the state variable through the props.
{payModal && <Pay setOpenPayModal={setPayModal} price={price} frequency={frequency} />}
I usually use the Context (useContext) when I need values and various components, for example:
I need to save the user id that is logged in to various components, and instead of getting it from the server every time I need it, I keep it saved in my context that I created, so I can make the call only once.
Documentation-> https://pt-br.reactjs.org/docs/context.html
I am new to react js and I am trying to get input value and set it to another html element (p)
And other thing is, I find it really hard to leran react js with all these complex syntaxes and things related to react js, are there any good tutorials that I can understand?
This is what I have been trying by researching all over the internet and I cannot go beyond this simple thing... Using jquery i could have done this so easily, and react js seems so complex to get the job done for some reason (maybe I am wrong). Anyway,
This is my SelectTrainsAndTickets.jsx
"use strict";
import React, { Component } from "react";
import { render } from "react-dom";
import "bootstrap/dist/css/bootstrap.css";
class SelectTrainsAndTickets extends Component {
constructor(props) {
super(props);
this.state={
noOfTickets: {document.getElementById('tickets')} //getting user input value
};
}
showDetails() {
this.setState({
noOfTickets:
});
console.log(this.state.noOfTickets);
}
render() {
return (
<React.Fragment>
<div className="container">
<div className="row">
<div className="col-md-2" />
<div className="col-md-4">
<select id="train" name="train" className="form-control">
<option value="select" disabled="disabled">
- Select Train -
</option>
<option value="1">Train A</option>
<option value="2">Train B</option>
<option value="3">Train C</option>
<option value="4">Train D</option>
</select>
</div>
<div className="col-md-4">
<input id="tickets"
className="form-control"
type="number"
placeholder="Number of tickets"
/>
</div>
<div className="col-md-2" />
</div>
<br />
<div className="row">
<div className="col-md-2" />
<div className="col-md-4" />
<div className="col-md-4 text-right">
<button
onClick={this.showDetails.bind(this)}
type="button"
className="btn btn-primary"
>
Buy Tickets
</button>
</div>
<div className="col-md-2">
<p>{this.state.noOfTickets}</p> //showing user input value
</div>
</div>
</div>
</React.Fragment>
);
}
}
export default SelectTrainsAndTickets;
Can someone tell me how to do this?
What you want to read up on is controlled inputs.
https://shripadk.github.io/react/docs/forms.html
There are only a few scenarios where you want to access the DOM directly and you almost never want to do it in your constructor as you are in your example.
First initialize your state you dont even need a constructor in this case.
class SelectTrainsandTIckets extends Component {
state = { ticketCount : 0, showTicketCount: false }
}
Since this is a controlled input you need to handle any change on that input. With events you get passed an event object where you can retrieve whatever the user is entering in your input. You want to store that change in the state variable you have setup.
handleChange(event){
this.setState({ticketCount : event.target.value})
}
Now you need to hook this all up to your actual input element in your render() method.
render() {
return(
<input name="myInput" onChange={handleChange.bind(this)} value={this.state.ticketCount} />
)
}
To display the current count you have already correctly displayed displayed the data but you want to hide the p element until the user clicks a button. Dont duplicate state especially for something like this. Simply hide the element until the user clicks a button.
<p style={this.state.showTicketCount ? {display: 'block'} : {display: 'none'}>{this.state.ticketCount}</p>
Finally the button to show the entered ticket count:
<button onClick={() => this.setState({showTicketCount: true})>Click me</button>
This is what I tried with help of the answers provided here. I got the idea and I changed it to the way I wanted it to work. This is my final answer:
"use strict";
import React, { Component } from "react";
import { render } from "react-dom";
import "bootstrap/dist/css/bootstrap.css";
class SelectTrainsAndTickets extends Component {
constructor(props) {
super(props);
this.state = {
noOfTickets: 0,
ticketCount: 0
};
this.haChange = this.haChange.bind(this);
this.getCount = this.getCount.bind(this);
}
haChange(event) {
this.setState({ noOfTickets: event.target.value });
}
getCount(){
this.setState({ticketCount: this.state.noOfTickets});
}
render() {
return (
<React.Fragment>
<div className="container">
<div className="row">
<div className="col-md-2" />
<div className="col-md-4">
<select id="train" name="train" className="form-control">
<option value="select" disabled="disabled">
- Select Train -
</option>
<option value="1">Train A</option>
<option value="2">Train B</option>
<option value="3">Train C</option>
<option value="4">Train D</option>
</select>
</div>
<div className="col-md-4">
<input onChange={this.haChange} className="form-control" type="number" placeholder="Number of tickets" />
</div>
<div className="col-md-2" />
</div>
<br />
<div className="row">
<div className="col-md-2" />
<div className="col-md-4" />
<div className="col-md-4 text-right">
<button onClick={this.getCount} type="button" value={this.state.ticketCount} className="btn btn-primary">
Buy Tickets
</button>
<p>{this.state.ticketCount === 0 ? '' : this.state.ticketCount}</p>
</div>
<div className="col-md-2" />
</div>
</div>
</React.Fragment>
);
}
}
export default SelectTrainsAndTickets;
I'm new in React and got stuck on how can I achieve this.
After the user fills the field CEP from the first card and click on the button 'Gravar Dados', it shows a second card with all the information from the first card.
Template Picture:
New Result Picture:
What I need is dynamically create another card, under the second card with new info from the first card.
This is what I've done so far.
I have this component DadosPessoais.js which is the second card in the Template Picture:
render() {
return (
<div className="card shadow mt-5" >
<div className="card-header">
<span>Dados Pessoais </span>
</div>
<div className="card-body">
<div className="row">
<div className="col-4">
<div>
<span>Cep: <strong>{this.props.cep}</strong></span>
</div>
<div>
<span>Bairro: <strong>{this.props.bairro}</strong></span>
</div>
</div>
<div className="col-5">
<div>
<span>Rua: <strong>{this.props.rua}</strong></span>
</div>
<div>
<span>Cidade: <strong>{this.props.cidade}</strong></span>
</div>
</div>
<div className="col-3">
<div>
<span>UF: <strong>{this.props.uf}</strong></span>
</div>
</div>
</div>
</div>
</div>
);
}
In my Home.js I have a form with onSubmit that calls mostrarDadosPessoais function:
Function:
mostrarDadosPessoais(e) {
e.preventDefault();
this.setState({
listaDados: this.state.listaDados.concat({
cep: e.target.value,
rua: e.target.value,
bairro: e.target.value,
cidade: e.target.value,
uf: e.target.value,
}),
});
}
And my input components UserInput, Should it be like this?:
<div className="col-3">
<UserInput name="Rua" Label="Rua" Id="Rua" value={this.state.rua} Disabled />
</div>
<div className="col-3">
<UserInput name="Bairro" Label="Bairro" Id="Bairro" value={this.state.bairro} Disabled />
</div>
<div className="col-3">
<UserInput name="Cidade" Label="Cidade" Id="Cidade" value={this.state.cidade} Disabled />
</div>
<div className="col-1">
<UserInput name="UF" Label="UF" Id="UF" value={this.state.uf} Disabled />
</div>
And to show the result card I've done this:
<div className="col-md-4">
{this.state.listaDados.length === 0
? null
: this.state.listaDados.map((lista) => (<DadosPessoais {...lista} />))}
</div>
This is my state:
this.state = {
listaCidade: [],
listaDados: [],
}
Any ideas on how can I create another component and keep the values from the first one?
Thank you.
If I understand well, you need a list of elements, displayed on the right, that will show user-entered data. You need the user to be able to enter many addresses through the form on the left, and you want each address to display on the right.
I would solve this with a component that contains state for all the elements you need to display in an array that you update on a form submit event, because that's an easy event to work with when you have forms with lots of fields. You can grab the values of the components through the event object e.
class StateContainer extends Component {
constructor() {
super(props);
this.state = {
addresses: [],
}
}
mostrarDadosPessoais(e) {
e.preventDefault();
this.setState({
listaDados: this.state.listaDados.concat({
cep: e.target.cep.value,
rua: e.target.rua.value,
// etc, etc
}),
})
}
render() {
return (
<div>
<form handleSubmit={this.mostrarDadosPessoais}>
{/* left-side form */}
<input name="msg" />
<button type="submit"></button>
</form>
<div>
{/* right-side list of elements */}
{this.state.addresses.length === 0
? null
: this.state.addresses.map(
(address) => (<DadosPessoais {...address} />)
)
}
</div>
</div>
);
}
}