I can't loop this array without getting an error (React) - arrays

I am learning React and I have just learned to get input values. I tried to write a code to practice. I had an error I can't solve. I tried everything:
I tried to put the array map in another function
I tried to use a return (but React isn't pure js so it didn't work)
I tried to move the map outside the functions but I need to map the Array each time I press the Add button
I tried to override the persons variables with the array map
Can someone help me? Thanks in advance.
P.S. Sorry for my terrible english
here it is:
import './App.css';
import React from 'react'
class App extends React.Component {
state = {
name: '',
surname: '',
age: '',
};
persons = []
getName = (e) => {
this.setState({name: e.target.value})
}
getSurname = (e) => {
this.setState({surname: e.target.value})
}
getAge = (e) => {
this.setState({age: e.target.value})
}
handleSubmit = (e) => {
e.preventDefault()
const name = this.state.name
const surname = this.state.surname
const age = this.state.age
this.persons.push(name + ' ' + surname + ', ' + age)
const persons_list = this.persons.map((person) =>
<li>{person}</li>
);
}
render () {
return (
<div className='container'>
<h1 className='title'>React Database</h1>
<form action="">
<input type="text" placeholder='Name' className='name' onChange={this.getName} />
<input type="text" placeholder="Surname" className='surname' onChange={this.getSurname} />
<input type="text" placeholder="Age" className='age' onChange={this.getAge} />
<button className='add_btn' onClick={this.handleSubmit}>Add</button>
<button className='delete_btn' onClick={this.persons.pop()}>Delete</button>
</form>
<ul>{this.persons_list}</ul> // the error is here: I can't use a variable inside a function
</div>
);
}
}
export default App

Move persons_list inside the render function. You have the list as part of the handleSubmit function.
handleSubmit = (e) => {
e.preventDefault()
const name = this.state.name
const surname = this.state.surname
const age = this.state.age
this.persons.push(name + ' ' + surname + ', ' + age)
const persons_list = this.persons.map((person) =>
<li>{person}</li>
);
}
//try something like this
import React, { Component } from "react";
import { render } from "react-dom";
class App extends Component {
state = {
name: "",
surname: "",
age: "",
porsons: [],
};
persons = [];
getName = (e) => {
this.setState({ name: e.target.value });
};
getSurname = (e) => {
this.setState({ surname: e.target.value });
};
getAge = (e) => {
this.setState({ age: e.target.value });
};
handleSubmit = (e) => {
e.preventDefault();
const name = this.state.name;
const surname = this.state.surname;
const age = this.state.age;
this.persons.push(name + " " + surname + ", " + age);
this.setState({ persons: this.persons });
};
render() {
console.log("App started");
return (
<div className="container">
<h1 className="title">React Database</h1>
<form action="">
<input
type="text"
placeholder="Name"
className="name"
onChange={this.getName}
/>
<input
type="text"
placeholder="Surname"
className="surname"
onChange={this.getSurname}
/>
<input
type="text"
placeholder="Age"
className="age"
onChange={this.getAge}
/>
<button className="add_btn" onClick={this.handleSubmit}>
Add
</button>
<button className="delete_btn" onClick={this.persons.pop()}>
Delete
</button>
</form>
<ul>
{(this.state.persons || []).map((person) => (
<li>{person}</li>
))}
</ul>
</div>
);
}
}
render(<App />, document.querySelector("#app"));

You should learn about scope. The persons_list variable you are using in handleSubmit is only visible to handleSubmit, you should move it outside the function, or instead do the following
render () {
return (
<div className='container'>
<h1 className='title'>React Database</h1>
<form action="">
<input type="text" placeholder='Name' className='name' onChange={this.getName} />
<input type="text" placeholder="Surname" className='surname' onChange={this.getSurname} />
<input type="text" placeholder="Age" className='age' onChange={this.getAge} />
<button className='add_btn' onClick={this.handleSubmit}>Add</button>
<button className='delete_btn' onClick={this.persons.pop()}>Delete</button>
</form>
<ul>{this.persons.map((person) => <li>{person}</li>)}</ul> // the error is here: I can't use a variable inside a function
</div>
);
}

Related

how to get value in dynamic input to sql React

I'm a newbie react.I had generated input fields dynamically. Now I want to get values firstName and Lastname in this dynamics input fields to the database but I don't know how to get this value.
please help me
his my code App.js
import React, { useState } from "react";
function App() {
const [inputList, setInputList] = useState([{ firstName: "", lastName: "" }]);
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = [...inputList];
list[index][name] = value;
setInputList(list);
};
const handleAddClick = () => {
setInputList([...inputList, { firstName: "", lastName: "" }]);
};
return (
<div className="App">
{inputList.map((x, i) => {
return (
<div className="box">
<input
name="firstName"
placeholder="Enter First Name"
value={x.firstName}
onChange={e => handleInputChange(e, i)}
/>
<input
className="ml10"
name="lastName"
placeholder="Enter Last Name"
value={x.lastName}
onChange={e => handleInputChange(e, i)}
/>
<div className="btn-box">
{inputList.length - 1 === i && <button onClick={handleAddClick}>Add</button>}
</div>
</div>
);
})}
<div style={{ marginTop: 20 }}>{JSON.stringify(inputList)}</div>
</div>
);
}
export default App;
Thank you
You need to retrieve values from server side which means you will do fetch process in browser side. Use useEffect to do fetch call and set the response with setInputList.
The below is one way with useEffect. Depending on your pj concept, you can also use useSwr, SSR or etc where data retrieving happens.
import React, { useState, useEffect } from "react";
function App() {
const [inputList, setInputList] = useState([{ firstName: "", lastName: "" }]);
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = [...inputList];
list[index][name] = value;
setInputList(list);
};
const handleAddClick = () => {
setInputList([...inputList, { firstName: "", lastName: "" }]);
};
useEffect(()=>{
let canExexute = true;
// Anything to retrieve data such as
// fetch, graphql query,,,etc
// Set values from the above
if (canExecute) setInputList(values)
// Caring about when unmounting component
return ()=>{
canExecute = false;
}
// Don't put inputList state itself as dependancies which causes infinite rendering.
},[dependencies])
return (
<div className="App">
{inputList.map((x, i) => {
return (
<div className="box">
<input
name="firstName"
placeholder="Enter First Name"
value={x.firstName}
onChange={e => handleInputChange(e, i)}
/>
<input
className="ml10"
name="lastName"
placeholder="Enter Last Name"
value={x.lastName}
onChange={e => handleInputChange(e, i)}
/>
<div className="btn-box">
{inputList.length - 1 === i && <button onClick={handleAddClick}>Add</button>}
</div>
</div>
);
})}
<div style={{ marginTop: 20 }}>{JSON.stringify(inputList)}</div>
</div>
);
}
export default App;

Passing values between components in React

I m beginner to reactJS and I m having so much trouble with self learning.
I want to print the data I get from first page.
I used 2 .js files
This is userpage.js:
import resultPage from "./resultPage";
// JS
//const input = document.getElementById('myText');
//const inputValue = input.value
// React
// value, onChange
const Multi = () => {
const [person, setPerson] = useState({ firstName: "", email: "", age: "" });
const [people, setPeople] = useState([]);
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setPerson({ ...person, [name]: value });
};
const handleSubmit = (e) => {
//e.preventDefault();
if (person.firstName && person.email && person.age) {
const newPerson = { ...person, id: new Date().getTime().toString() };
setPeople([...people, newPerson]);
setPerson({ firstName: "", email: "", age: "" });
resultPage(people, person);
}
};
return (
<>
<article className="form">
<form>
<div className="form-control">
<label htmlFor="firstName">Name : </label>
<input
type="text"
id="firstName"
name="firstName"
value={person.firstName}
onChange={handleChange}
/>
</div>
<div className="form-control">
<label htmlFor="email">Email : </label>
<input
type="email"
id="email"
name="email"
value={person.email}
onChange={handleChange}
/>
</div>
<div className="form-control">
<label htmlFor="age">Age : </label>
<input
type="number"
id="age"
name="age"
value={person.age}
onChange={handleChange}
/>
</div>
<button type="submit" className="btn" onClick={handleSubmit}>
add person
</button>
</form>
</article>
</>
);
};
export default Multi;
This has 2 textboxes and a submit button.
This code is from resultPage.js:
function resultPage(people, person) {
return (
<article>
{people.map((person) => {
const { id, firstName, email, age } = person;
return (
<div key={id} className="item">
<h4>{firstName}</h4>
<p>{email}</p>
<p>{age}</p>
</div>
);
})}
</article>
);
}
export default resultPage;
What am I doing wrong? I m new to reactjs. So kindly spare my obvious mistakes and help me.
From React documentation
HTML form elements work a bit differently from other DOM elements in React, because form elements naturally keep some internal state.
You need to add handleSubmit to your form, and it'll work. As #Halcyon suggested, using a Capital case for a component is good. It's tough to distinguish between HTML elements and components if you use lowercase. Read this for more details.
I am attaching a working sandbox for your code.
You're calling resultPage in handleSubmit. That's not going to work. You want resultPage to be part of the rendering, probably conditionally.
Consider something like:
return <>
{person.firstName !== "" && <resultPage people={people} person={person} />}
{person.firstName === "" && <>
// the form
</>}
</>;
Also since resultPage is a component, it's best to capitalize it.
I think you probably want a 3rd component:
const MyComponent = () => {
const [ people, setPeople ] = React.useState([]);
const [ isEditing, setIsEditing ] = React.useState(false);
return <>
{isEditing && <Multi setPeople={(people) => {
setPeople(people);
setIsEditing(false);
}}
{isEditing === false && <resultPage people={people} />}
</>;
}
Mutli now accepts a prop setPeople that is called in handleSubmit.

Losing focus on input onChange React

What I'm trying to achieve, it's to create each input as own component.
And I can't get how to fix thing when my app all the time rerendering when I'm pressing any key.
I know if I'll use controlledInputs -> so each input would have it own useState that would work. But the main idea to do that this way
import React, { useState } from 'react';
const ControlledInputs = () => {
const [person, setPerson] = useState({ firstName: '', email: '', age: '' });
const [people, setPeople] = useState([]);
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setPerson({ ...person, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
if (person.firstName && person.email && person.age) {
const newPerson = { ...person, id: new Date().getTime().toString() };
setPeople([...people, newPerson]);
setPerson({ firstName: '', email: '', age: '' });
}
};
function FormControl({number, idname , type , text}){
return(
<div className='form-control'>
<label htmlFor={idname}>{text} : </label>
<input
type={type}
id={idname}
name={idname}
value={person[idname]}
onChange={(e) =>handleChange(e)}
/>
</div>
)
}
return (
<>
<article className='form'>
<form>
<FormControl idname={"firstName"} type={"text"} text={"First name"}/>
<FormControl type={"email"} idname={"email"} text={"Email"}/>
<FormControl type={"age"} idname={"age"} text={"Age"}/>
<button type='submit' className='btn' onClick={handleSubmit}>
add person
</button>
</form>
</article>
<article>
{people.map((person) => {
const { id, firstName, email, age } = person;
return (
<div key={id} className='item'>
<h4>{firstName}</h4>
<p>{email}</p>
<p>{age}</p>
</div>
);
})}
</article>
</>
);
};
export default ControlledInputs;
You need to define the FormControl outside of ControlledInputs, otherwise, React will recreate it and you lose focus as well as data.
And you need to pass value and handleChange as props in FormControl.
Here are codes you can refactor. Please note that the number you defined is not removed.
function FormControl({value, handleChange, idname , type , text}){
return(
<div className='form-control'>
<label htmlFor={idname}>{text} : </label>
<input
type={type}
id={idname}
name={idname}
value={value}
onChange={(e) =>handleChange(e)}
/>
</div>
)
}
Usage in form:
function FormControl({value, handleChange, idname , type , text}){
return(
<div className='form-control'>
<label htmlFor={idname}>{text} : </label>
<input
type={type}
id={idname}
name={idname}
value={value}
onChange={(e) =>handleChange(e)}
/>
</div>
)
}
const ControlledInputs = () => {
const [person, setPerson] = useState({ firstName: '', email: '', age: '' });
const [people, setPeople] = useState([]);
const handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
setPerson({ ...person, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
if (person.firstName && person.email && person.age) {
const newPerson = { ...person, id: new Date().getTime().toString() };
setPeople([...people, newPerson]);
setPerson({ firstName: '', email: '', age: '' });
}
};
return (
<>
<article className='form'>
<form>
<FormControl idname={"firstName"} type={"text"} text={"First name"} value={person['firstName']} handleChange={handleChange}/>
<FormControl type={"email"} idname={"email"} text={"Email"} value={person['email']} handleChange={handleChange}/>
<FormControl type={"age"} idname={"age"} text={"Age"} value={person['age']} handleChange={handleChange}/>
<button type='submit' className='btn' onClick={handleSubmit}>
add person
</button>
</form>
</article>
<article>
{people.map((person) => {
const { id, firstName, email, age } = person;
return (
<div key={id} className='item'>
<h4>{firstName}</h4>
<p>{email}</p>
<p>{age}</p>
</div>
);
})}
</article>
</>
);
};
That is because FormControl is defined inside the ControlledInputs component. So, on each rerender of the ControlledInputs, you create a new FormControl function and that means that React will treat it as a different component than the one in the previous render, and so it will lose focus, as the old one is considered unmounted.
Just define the FormControl outside the other one, and pass it what extra data you need as props, and you should be set.

React - Pass form data into a function

I am new to React and I have a dashboard to display all users. I created a new feature to add a new user on the dashboard. Currently there is only one field to fill out which is the email.
Dashboard.js
<form id="new-user-form">
<fieldset>
<label>
<p>Email</p>
<input type="email" name="new-user" id="new-user" required />
</label>
</fieldset>
<button
type="button"
form="new-user-form"
onClick={this.onFormSubmit}
>
Add
</button>
</form>
and the method onFormSubmit function
onFormSubmit(event) {
var new_email = document.getElementById("new-user").value;
var today =
new Date().getFullYear() +
"-" +
("0" + (new Date().getMonth() + 1)).slice(-2) +
"-" +
("0" + new Date().getDate()).slice(-2);
let new_users = {
email: new_email,
registration: today,
status: "disabled",
};
this.setState((prevState) => ({
users: [...prevState.users, new_users],
}));
What I currently have works but there is no data-validation, I can add a user with an empty form because the new_email variable is taking document.getElementById("new-user").value which can be empty and should rather be the form data.
If you wanna see my full code for dashboard.js
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
users: [
{
email: "pierre-alexandre#gmail.com",
registration: "2020-10-25",
status: "active",
},
{
email: "antoine373#gmail.com",
registration: "2018-10-22",
status: "active",
},
{
email: "sofia.leduc#gmail.com",
registration: "2020-01-03",
status: "disabled",
},
],
searchEmail: "",
};
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit(event) {
var new_email = document.getElementById("new-user").value;
var today =
new Date().getFullYear() +
"-" +
("0" + (new Date().getMonth() + 1)).slice(-2) +
"-" +
("0" + new Date().getDate()).slice(-2);
let new_users = {
email: new_email,
registration: today,
status: "disabled",
};
this.setState((prevState) => ({
users: [...prevState.users, new_users],
}));
}
handleLogout = () => {
fire.auth().signOut();
};
handleInput = (e) => {
this.setState({ searchEmail: e.target.value });
};
render() {
let filteredUsers = this.state.users.filter((user) => {
return user.email
.toLowerCase()
.includes(this.state.searchEmail.toLowerCase());
});
return (
<div>
<h2>Welcome</h2>
<button onClick={this.handleLogout}>Logout</button>
<div className="users-dashboard-container">
<div className="users-dashboard">
<div className="top-navigation">
<div className="search-navigation">
<img
src={process.env.PUBLIC_URL + "images/search.png"}
alt="logo"
/>
<SearchBox handleInput={this.handleInput} />
</div>
<div className="add-user">
<a>Add user</a>
</div>
</div>
<div class="dashboard-content">
<table>
<tr>
<th>Email</th>
<th>Registration Date</th>
<th>Status</th>
<th>Other</th>
</tr>
<UserList filteredUsers={filteredUsers} />
</table>
</div>
</div>
</div>
<div>
<div className="wrapper">
<h1>Add a User</h1>
<form id="new-user-form">
<fieldset>
<label>
<p>Email</p>
<input type="email" name="new-user" id="new-user" required />
</label>
</fieldset>
<button
type="button"
form="new-user-form"
onClick={this.onFormSubmit}
>
Add
</button>
</form>
</div>
</div>
</div>
);
}
}
export default Dashboard;
You can try like below.
handleChange = e => { // You can use this function to handle all inputs with name as state property
this.setState({ [e.target.name]: e.target.value }) // name will be `email`
}
formSubmit = () => {
if (!this.state.email) {
// Do email validations here
alert('Enter Email')
return
}
const today =
new Date().getFullYear() +
"-" +
("0" + (new Date().getMonth() + 1)).slice(-2) +
"-" +
("0" + new Date().getDate()).slice(-2);
const email = this.state.email
const new_user = {
email,
registration: today,
status: "disabled",
};
this.setState((prevState) => ({
users: [...prevState.users, new_user],
email: '' // Resets email once added into dashboard
}));
}
<div className="wrapper">
<h1>Add a User</h1>
<label>
<p>Email</p>(
<input
type="email"
name="email" // <- this will be used add state key
value={email}
onChange={handleChange} />
</label>
<button onClick={this.onFormSubmit}>
Add
</button>
</div>
One simple way is to disable the submit button if state.searchEmail is empty.
import React, { Component } from "react";
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
searchEmail: ""
};
this.onFormSubmit = this.onFormSubmit.bind(this);
this.handleInput = this.handleInput.bind(this);
}
onFormSubmit() {
let new_user = {
email: this.state.searchEmail
};
console.log(new_user);
}
handleInput(e) {
this.setState({ searchEmail: e.target.value });
};
render() {
return (
<form>
<label>
Email
<input
onChange={this.handleInput}
value={this.state.searchEmail}
type="email"
required
/>
</label>
<button
onClick={this.onFormSubmit}
disabled={this.state.searchEmail === ""}
>
Add
</button>
</form>
);
}
}
export default Dashboard;
You could take this approach further and move the validation into a function where you check state.searchEmail against a regex.
import React, { Component } from "react";
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
searchEmail: ""
};
}
onFormSubmit = (e) => {
e.preventDefault();
let new_user = {
email: this.state.searchEmail
};
console.log(new_user);
this.setState({ searchEmail: "" });
};
handleInput = (e) => {
this.setState({ searchEmail: e.target.value });
};
formIsInvalid = () => {
return (
this.state.searchEmail === "" ||
!this.state.searchEmail.match(/^[\w-.]+#([\w-]+\.)+[\w-]{2,4}$/)
);
};
render() {
return (
<form action="" onSubmit={this.onFormSubmit}>
<label>
Email
<input
onChange={this.handleInput}
value={this.state.searchEmail}
type="email"
required
/>
</label>
<button disabled={this.formIsInvalid()}>Add</button>
</form>
);
}
}
export default Dashboard;

Unable to use onClick event ReactJS

I am trying to send data in a form using onClick. Below is my Form
import React from 'react';
const form = props => (
<form>
<input type = "text" name = "name" placeholder="name" />
<input type = "text" name = "lastname" placeholder="last name" />
<input type = "text" name = "id" placeholder="email address" />
<button onClick = {props.clickHandler}>Send</button>
</form>
);
export default form;
Below is my App.js
import React, { Component } from 'react';
import './App.css';
import Form from './Components/Form';
class App extends Component {
clickHandler = async(e) =>{
e.preventDefault();
const name = e.target.value;
console.log(name + 'was typed');
}
render() {
return (
<div className="App">
<h1>I am a React App </h1>
<Form form = {this.Form}/>
</div>
);
}
}
export default App;
I am not sure why the value is not being visible on console. Any help is appreciated.
First of all name your components as starting with a capital letter.
For this kind of situations, you can keep the values in your main component's state, then update them via your callback functions which are being passed to the child component.
class App extends React.Component {
state = {
name: "",
lastName: "",
email: ""
};
clickHandler = e => {
e.preventDefault();
const { name, lastName, email } = this.state;
const data = `Data is: ${name}-${lastName}-${email}`;
alert(data);
};
changeHandler = e => {
e.preventDefault();
const { name, value } = e.target;
this.setState({
[name]: value
});
};
render() {
console.log(this.state);
const { clickHandler, changeHandler } = this;
return (
<div className="App">
<h1>I am a React App </h1>
<Form clickHandler={clickHandler} changeHandler={changeHandler} />
</div>
);
}
}
const Form = props => (
<form>
<input
onChange={props.changeHandler}
type="text"
name="name"
placeholder="name"
/>
<input
onChange={props.changeHandler}
type="text"
name="lastName"
placeholder="last name"
/>
<input
onChange={props.changeHandler}
type="text"
name="email"
placeholder="email address"
/>
<button onClick={props.clickHandler}>Send</button>
</form>
);
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
For the inputs, you need an onChange handler, for your button you need an onClick handler as you thought. You can see the changing state in the console. When you hit the button it creates an object using your state values then fires the alert.
You need to pass down the clickHandler as a prop to your Form component
render() {
return (
<div className="App">
<h1>I am a React App </h1>
<Form onSubmit={this.clickHandler} />
</div>
)
}
Your are using React Uncontrolled form inputs so you need to handle the form onSubmit event
import React from "react"
const form = props => (
<form
onSubmit={e => {
e.preventDefault()
this.props.onSubmit(e)
}}
>
<input type="text" name="name" placeholder="name" />
<input type="text" name="lastname" placeholder="last name" />
<input type="text" name="id" placeholder="email address" />
<input type="submit" value="Send" />
</form>
)
export default form
And in the clickHandleryou can now retrieve all values from the submit event like so
clickHandler = async e => {
e.preventDefault()
const name = e.target.elements[0]
console.log(name + "was typed")
}
Edit: Added a way to see form values
import React, { Component } from 'react';
import './App.css';
import Form from './Components/Form';
class App extends Component {
clickHandler = (data) => {
console.log('data::', data );
}
render() {
return (
<div className="App">
<h1>I am a React App </h1>
<Form clickHandler = {this.clickHandler}/>
</div>
);
}
}
export default App;
import React, { PureComponent } from 'react';
export class Form extends PureComponent {
state = {
name: '',
lastname: '',
id: ''
}
onNameChange = (event) => {
this.setState({
name: event.target.value
})
}
onLastNameChange = (event) => {
this.setState({
lastname: event.target.value
})
}
onEmailChange = (event) => {
this.setState({
id: event.target.value
})
}
render() {
return (
<form>
<input type = "text" name = "name" placeholder="name" onChange={this.onNameChange} />
<input type = "text" name = "lastname" placeholder="last name" onChange={this.onLastNameChange} />
<input type = "text" name = "id" placeholder="email address" onChange={this.onEmailChange}/>
<button onClick = {props.clickHandler.bind(null, this.state)}>Send</button>
</form>
);
}
}

Resources