How to pass data between components in react? - javascript

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;

Related

React form input values in JS

I am using NextJS with bulma CSS to create a simple application. I have this following form:
const MyPage = () => {
const [firstName, setFirstName] = useState('')
const [secondName, setSecondName] = useState('')
const updateFirstName = event => {
setFirstName(event.target.value)
}
const updateSecondName = event => {
setSecondName(event.target.value)
}
const createUser = async() => {
// Todo: perform some action with firstName and secondName
}
return (
<section className='mt-5'>
<div className='container'>
<div className='field'>
<label className='label'>My Form</label>
<div className='control'>
<input onChange={updateFirstName} className='input' type='type' placeholder='Enter First Name'></input>
</div>
</div>
<div className='field'>
<div className='control'>
<input onChange={updateSecondName} className='input' type='type' placeholder='Enter Second Name'></input>
</div>
</div>
<button onClick={createUser} className='button is-primary'>Create</button>
</div>
</section>
)
}
export default MyPage
I have to call updateFirstName and updateSecondName on every input change.
I want to get these input field's value on createUser() function call only. Please suggest how to do it or any other better approach. I want to eliminate firstName and secondName variables, and directly access entered input in the createUser() function.
If you don't want a controlled input. You can quit managing the state and access the value old way using plain vanilla JS.
Make sure to add name attribute with all the input fields.
function createUser() {
const inputs = document.querySelectorAll(".field input")
let data = {}
inputs.forEach(input => {
data[input.name] = input.value
})
/**
This would yield you
{
'firstname': 'value',
'secondName': 'value'
}
**/
}
Please change your input fields as shown below:
<input onChange={(e)=>createUser(e,'firstName')} className='input' type='type' placeholder='Enter First Name'></input>
<input onChange={(e)=>createUser(e,'lastName')} className='input' type='type' placeholder='Enter First Name'></input>
Then in your update your createUser function as shown below:
const createUser = (event, element) => {
if(element==='firstName') {
setFirstName(event.target.value)
}
if(element==='lastName') {
setLastName(event.target.value)
}
}
You can try alternatively with this useRef() hook,
const MyPage = () => {
const firstName = useRef();
const secondaName = useRef();
const createUser = async() => {
// Todo: perform some action with firstName and secondName
console.log(firstName.current.value, secondName.current.value) // It will prints the value that is typed by the user in both the textfields
}
return (
<section className='mt-5'>
<div className='container'>
<div className='field'>
<label className='label'>My Form</label>
<div className='control'>
<input ref={firstName} className='input' type='type' placeholder='Enter First Name'></input>
</div>
</div>
<div className='field'>
<div className='control'>
<input ref={secondName} className='input' type='type' placeholder='Enter Second Name'></input>
</div>
</div>
<button onClick={createUser} className='button is-primary'>Create</button>
</div>
</section>
)
}
export default MyPage
You can write a handler function
Firstly, you should add all variables to same state.
const [userInfo, setUserInfo] = useState({
firstName: "",
secondName: ""
});
and you should give a name to inputs like this.
<input
className="input"
onChange={onChangeHandler}
name="firstName" //name attribute must same your state variable
placeholder="Enter First Name"
/>
<input
className="input"
onChange={onChangeHandler}
name="secondName" //name attribute must same your state variable
placeholder="Enter Second Name"
/>
and your handler function should like this
const onChangeHandler = (e) =>
setUserInfo({ ...userInfo, [e.target.name]: e.target.value });
and this function take your input value and set your state who same name.
Full code
export default function App() {
const [userInfo, setUserInfo] = useState({
firstName: "",
secondName: ""
});
const onChangeHandler = (e) =>
setUserInfo({ ...userInfo, [e.target.name]: e.target.value });
const sendData = () => {
console.log(userInfo);
};
return (
<div className="App">
<section className="mt-5">
<div className="container">
<div className="field">
<label className="label">My Form</label>
<div className="control">
<input
className="input"
onChange={onChangeHandler}
name="firstName"
placeholder="Enter First Name"
/>
</div>
</div>
<div className="field">
<div className="control">
<input
className="input"
onChange={onChangeHandler}
name="secondName"
placeholder="Enter Second Name"
/>
</div>
</div>
<button onClick={sendData} className="button is-primary">
Create
</button>
</div>
</section>
</div>
);
}
https://codesandbox.io/s/gallant-pasteur-uglbri?file=/src/App.js:58-1264

setting the value of more than one input

I am trying to build a login form. I am trying to set up the value of the email & password field individually. But as soon as I try to enter the text in the email text field, the same appears in the password field too. Can I have a solution to this?
Below is the code.
I guess the error is in OnChange fn where I am assigning the same value e.target.value to both the {email, passwaord}.
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
const LoginPage = () => {
let navigate = useNavigate();
const [credentials, setCredentials] = useState({email:"",password:""});
const onChange = (e) => {
setCredentials({email: e.target.value ,password: e.target.value})
console.log(credentials.email, credentials.password)
}
const goToSignUp = () => {
navigate("/signup");
}
return (
<>
<div className="container my-5">
<div id="loginbody">
<div className="mt-3">
<h2 className="my-3 display-3">Login Here</h2>
<form className="login-form p-5">
<div className="mb-3">
<label for="exampleInputEmail1" className="form-label">
Email address
</label>
<input
type="email"
className="form-control"
id="email"
name="email"
value={credentials.email}
aria-describedby="emailHelp"
onChange={onChange}
/>
<div id="emailHelp" className="form-text">
We'll never share your email with anyone else.
</div>
</div>
<div className="mb-3">
<label for="exampleInputPassword1" className="form-label">
Password
</label>
<input
type="password"
className="form-control"
id="password"
name="password"
value={credentials.password}
onChange={onChange}
/>
</div>
<div className="d-grid gap-2 my-4 col-6 mx-auto">
<button type="submit" className="btn btn-success">
Submit
</button>
</div>
<hr />
<div className="mb-3 text-center">
<div id="emailHelp" className="form-text center my-3">
Didn't have an account ?
</div>
<div className="d-grid gap-2 my-3 col-6 mx-auto">
<button onClick={goToSignUp} className="btn btn-success ">
SignUp Here !
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default LoginPage;
You have identified the problem. You need to pass the key to change as well.
Here passing a callback to setState which provides the current state as a parameter, cloning the state object using spread syntax, and then updating the relevant property in the copied object using the passed key as a computed property name.
const LoginPage = () => {
const [credentials, setCredentials] = useState({email:"",password:""});
const onChange = (e, key) => {
setCredentials(prevCredentials => ({...prevCredentials, [key]: e.target.value}))
}
return (
<>
//...
<input
type="email"
className="form-control"
id="email"
name="email"
value={credentials.email}
aria-describedby="emailHelp"
onChange={(e) => onChange(e, 'email')}
/>
//...
<input
type="password"
className="form-control"
id="password"
name="password"
value={credentials.password}
onChange={(e) => onChange(e, 'password')}
/>
//...
</>
);
};
Note: Calling console.log() right after setting state will not log the updated state, the new state values won't be available until the next render cycle. see: useState set method not reflecting change immediately
Use the proper key to the respective fields
const onChange = (e) => {
setCredentials({ ...credentials, [e.target.name]: e.target.value})
console.log(credentials);
}

Displaying a Form From OnClick Button

I am trying to display a form as a popup when I click on a button. I am new to React to I don't really know where to start.
This is the React Component I want to display:
import React, {useState} from 'react';
const initialFormValues = {
firstName: '',
lastName: '',
email: '',
phone: '',
message: ''
}
export default function Contact(){
//set up state
const [contacts, setContacts] = useState([]);
const [formValues, setFormValues] = useState(initialFormValues);
//helper function to trach changes in the input
const inputChange= (name, value) => {
setFormValues({...formValues, [name]: value})
}
const onChange = e => {
const {name, value} = e.target;
inputChange(name, value);
}
//post a new contact to the backend eventually, right now just display the contact to the DOM
const postNewContact = newContact => {
setContacts([newContact, ...contacts])
setFormValues(initialFormValues);
}
const formSubmit = () => {
const newContact = {
firstName: formValues.firstName.trim(),
lastName: formValues.lastName.trim(),
email: formValues.email.trim(),
phone: formValues.phone.trim(),
message: formValues.message.trim()
}
postNewContact(newContact)
}
const onSubmit = e => {
e.preventDefault();
formSubmit();
}
return (
<div className='contact container'>
<h1>This is the contact component</h1>
<form id='contact-form' onSubmit={onSubmit}>
<div className='form-group submit'>
<button id='contact-btn'>SUBMIT CONTACT INFO</button>
</div>
<div className='form-group inputs'>
<label>
First Name:
<input
type='text'
value={formValues.firstName}
onChange={onChange}
name="firstName"
/>
</label>
<label>
Last Name:
<input
type='text'
value={formValues.lastName}
onChange={onChange}
name="lastName"
/>
</label>
<label>
Email:
<input
type='email'
value={formValues.email}
onChange={onChange}
name="email"
/>
</label>
<label>
Phone Number:
<input
type='text'
value={formValues.phone}
onChange={onChange}
name="phone"
/>
</label>
<label>
Message:
<input
type='text'
value={formValues.message}
onChange={onChange}
name="message"
/>
</label>
</div>
</form>
</div>
)
}
I want to display this component as a popup when I click on a "Contact Us" link:
import React from 'react';
import './footer.css'
function Footer()
{
return (
<div className="Footer">
<div className="container">
<div className="row">
<div className="col">
<h4 className="block ">About Us</h4>
</div>
<div className="col">
<h4 className="block" >Contact</h4>
</div>
<div className="col">
<h4><a className="block" href={"https://www.cdc.gov/coronavirus/2019-ncov/faq.html"}>COVID-19 FAQ</a></h4>
</div>
<div className="col">
<h4 className="block"><a className="block" href={"https://www.cdc.gov/coronavirus/2019-ncov/if-you-are-sick/quarantine.html"}>CDC Guidelines</a></h4>
</div>
</div>
</div>
</div>
);
}
export default Footer;
How would I accomplish this? I want the popup to display on click with the following form, and I want the popup to disappear when the user clicks the submit button.
You can either create a popup using HTML + CSS + Javascript as in the link
Custom Popup or use any UI framework like Ant Design and make use of the Modal component.
btn.onclick = function() {
modal.style.display = "block";
}
In react, components are controlled by states in general.
What you will need for toggling the contact form visibility is:
state / setState for the contact form visibility (true / false)
function that triggers state change of the contact form visibility
set the function above at a button with onClick property
With the visibility state, you can set a conditional statement whether the contact form will set the style of display: none or block.

Why css file of one component interacted with the css file of another component in ReactJS? How to handle it?

I am trying to make a website template with Reactjs. In the Jumbotron section i make subscription form and in the home section User Entry form. But the css of one component interacted with another's one. How can i handle it?
[1]: https://i.stack.imgur.com/Wd4OQ.png
User EntryJs:-
import React, { Component } from 'react'
import './User Entry.css'
class Form extends Component {
initialState = {
name: "",
age: "",
job: ""
}
state = this.initialState
changeHandler = event => {
const { name, value } = event.target
this.setState({
[name]: value
})
}
render() {
const { name, job, age } = this.state
return (
<form className="form-inline">
<div className="row">
<div className="col-md-3">
<div className="form-group">
<label htmlFor="name">Name:-</label>
<input type="text"
className="form-control"
name="name"
id="name"
value={name}
autoFocus
onChange={this.changeHandler} />
</div>
</div>
<div className="col-md-3">
<div className="form-group">
<label htmlFor="age">Age:-</label>
<input type="text"
className="form-control"
name="age"
id="age"
value={age}
autoFocus
onChange={this.changeHandler} />
</div>
</div>
<div className="col-md-3">
<div className="form-group">
<label htmlFor="job">Job:-</label>
<input type="text"
className="form-control"
name="job"
id="job"
value={job}
autoFocus
onChange={this.changeHandler} />
</div>
</div>
<div className="col-md-3"></div>
</div>
</form>
)
}
}
export default Form
Header JS:-
import React, { Component } from 'react'
import './Header.css'
import { Link, withRouter } from "react-router-dom";
class Header extends Component {
constructor(props) {
super(props)
this.state = {
email: ""
}
}
submitHandler = event => {
event.preventDefault();
alert(`Subscribed Email is : ${this.state.email}`);
}
changeHandler = event => {
this.setState({
email: event.target.value
})
}
render() {
return (
// Navbar Starts
<div>
<div className="row navbar">
<Link to="/" style={{textDecoration:'none'}}><div className="col-md-2 logo">ReactApp</div></Link>
<div className="col-md-6"></div>
<Link to="/" style={{textDecoration:'none'}}> <div className="col-md-1 link"> Home</div> </Link>
<Link to="/about" style={{textDecoration:'none'}}> <div className="col-md-1 link"> About</div> </Link>
<Link to="/counter" style={{textDecoration:'none'}}> <div className="col-md-1 link"> Counter </div></Link>
<Link style={{textDecoration:'none'}}><div className="col-md-1 link">Login</div></Link>
</div>
<div className="jumbotron text-center">
<h1>React-App</h1>
<p>We specialize in <strong>Web Development</strong></p>
{/* Subscribing form starts*/}
<form className="form-inline subscribingForm" onSubmit={this.submitHandler}>
<div className="input-group">
<input type="email"
className="form-control"
value={this.state.email}
onChange={this.changeHandler}
size="80"
placeholder="Email..."
required />
<div className="input-group-btn">
<input type="submit" value="Subscribe" className="subscribingBtn" />
</div>
</div>
</form>
{/* Subscribing form closes*/}
</div>
</div>
)
}
}
export default withRouter(Header);
Where is the .css file loaded, in the root component? It probably is loaded globally and is used on every component.Better use JSS (https://cssinjs.org/?v=v10.3.0)
In general react transpiles all the css and add it in to tag.
And as result you one file css conflicts with other.
If you want to avoid this, you can use modular css.
https://create-react-app.dev/docs/adding-a-css-modules-stylesheet/

show last searched items below search form

I have a search form which is developed using redux form. I have used router to route to the form. After i submit the data from search form and revert back to the same form, i want to show a list of data that had been searched before whenever user clicks on search places input box. How can i do so?
Like in the image
Here is my code
const Banner = (props) => (
<Router>
<div className="container banner">
<ServiceType />
<div className="row">
<Match exactly pattern="/" location={props.location} component={Apartamentos} />
<Match pattern="/apartamentos" component={Apartamentos} />
<Match pattern="/coche" component={Coche} />
<Match pattern="/experiencias" component={Experiencias} />
</div>
</div>
</Router>
);
const renderGeoSuggestField = ({
input,
location
}) => (
<Geosuggest
fixtures={fixtures}
initialValue={input.value.label}
inputClassName="form-control destino"
onChange={(value) => input.onChange(value)}
onSuggestSelect={(value) => input.onChange(value)}
radius="20"
/>
);
const renderDateRangePicker = ({
input,
focusedInput,
onFocusChange,
}) => (
<DateRangePicker
onDatesChange={(start, end) => input.onChange(start, end)}
startDate={(input.value && input.value.startDate) || null}
endDate={(input.value && input.value.endDate) || null}
minimumNights={0}
/>
);
class ServiceType extends Component {
render() {
return(
div className="col-xs-12 col-sm-12 col-md-4 serviceImg">
<Link to="/apartamentos">
<img
src={imageUrl}
alt="apartamentos"
className="img-responsive"
/>
<h4>APARTAMENTOS</h4>
</Link>
</div>
);
}
}
class Apartamentos extends Component {
render() {
const { focusedInput } = this.state;
return (
<div className="form-box text-center">
<div className="container">
<form className="form-inline">
<div className="form-group">
<Field
name='geoSuggest'
component={renderGeoSuggestField}
onChange={(value) => this.onChange(value)}
onSuggestSelect={(suggest) => this.onSuggestSelect(suggest)}
location={new google.maps.LatLng(53.558572, 9.9278215)}
/>
</div>
<div className="form-group">
<Field
name="daterange"
onFocusChange={this.onFocusChange}
focusedInput={focusedInput}
component={renderDateRangePicker}
/>
</div>
<div className="form-group">
<Field
name="persona"
component="select"
>
<option>1 persona</option>
<option>2 personas</option>
</Field>
</div>
<button type="submit" className="btn btn-default buscar">BUSCAR</button>
</form>
</div>
</div>
);
}
}
const ApartmentForm = reduxForm({
form: 'ApartmentForm',
destroyOnUnmount: false,
})(Apartamentos);
What you should do is maintain is a redux state variable called say previousSearches which is initialized as an empty array. Everytime you click Submit push the form data into this previousSearches array. So when you click on the input button next just display all information from the previousSearches array (which is a redux variable and can be accessed as a prop).
Something like this I guess
case 'ADD_PREVIOUS_SEARCH':
return Object.assign({}, state, {
previousSearches: state.previousSearches.push(action.search)
})
Then you can just access previousSearches by this.props.previousSearches

Categories

Resources