validate a form in the parent component react - javascript

I have a two inputs in my child component, I would like to pass the event of the inputs to my parent component. In the parent component I have a function which will handle the submission. I have the two useState in the parent component, just in case I need to use it in the future in other condition to ensure the user is logged in. I am wondering how to achieve this ? or am I taking the wrong approach with having the usestates in my parent component ?
import { useState } from "react";
import Child from './Child'
import "./styles.css";
export default function Parent() {
const [login, setLogin] = useState(null);
const [password, setPassword] = useState(null);
const loginhandler = ()=>{
if (!login && !password){
console.log("alert error")
} else {
console.log('you are logged in')
}
}
return (
<>
<Child/>
</>
);
}
import { useState } from "react";
import "./styles.css";
export default function Parent() {
const [login, setLogin] = useState(null);
const [password, setPassword] = useState(null);
return (
<>
<input
placeholder="Id"
value={login}
onChange={(e) => setLogin(e.target.value)}
/>
<input
placeholder="Password"
value={password}
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
</>
);
}

you can pass function like this:
const setParentState = (val) => setLogin;
how do you pass this function to the child component:
<Child setParentPassword={setParentState} />
this way you can use the child components variables and use or set it in parent state

Flow should be like this :
import { useState } from "react";
import Child from './Child'
import "./styles.css";
export default function Parent() {
const [login, setLogin] = useState(null);
const [password, setPassword] = useState(null);
const loginhandler = ()=>{
//your logic will goes here
if (!login && !password){
console.log("alert error")
} else {
console.log('you are logged in')
}
}
return (
<>
<Child
loginhandler={loginhandler}
setLogin={setLogin}
setPassword={setPassword}
/>
</>
);
}
import { useState } from "react";
import "./styles.css";
export default function Child({loginhandler, setLogin, setPassword}) {
return (
<>
<input
placeholder="Id"
value={login}
onChange={(e) => setLogin(e.target.value)}
/>
<input
placeholder="Password"
value={password}
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={loginhandler}>LOGIN</button>
</>
);
}

Related

Share State between two components [ Hooks ]

I need help, is there any possible way to send the useEffect submittedInput from search.js to AllThemeContext.js to use it as value of Provider ?
Both are in two different folders [adjacent components]
/Search Js/
/*Import*/
import React, { useState } from "react";
import "./Search.scss";
/*Component*/
const Search = () => {
/************************************************************************************************/
/***************************************************The Logic Area/
/****************************************************************/
/*The state used*/
const [input, setInput] = useState("");
const [submittedInput, setSubmittedInput] = useState("");
/*Functions used*/
// onFormSubmit
const onFormSubmit = (e) => {
e.preventDefault();
setInput("");
};
/************************************************************************************************/
/*************************************************** The Return Area/
/****************************************************************/
return (
<>
<div className="Search">
<form onSubmit={onFormSubmit} className="Search__form">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
type="text"
placeholder=" Title, companies, expertise, or benefits"
style={{ fontFamily: "Poppins, FontAwesome" }}
></input>
<button onClick={() => setSubmittedInput(input)}>Search</button>
</form>
</div>
</>
);
};
/************************************************************************************************/
/***************************************************The Component End Area/
/****************************************************************/
export default Search;
/All Theme context/
import React, { createContext, useState } from "react";
export const AllContext = createContext();
/*Provider*/
const AllContextProvider = (props) => {
const [input, setInput] = useState();
const [numbs, setNumbs] = useState(1);
return (
<AllContext.Provider value={{ input, numbs }}>
{props.children}
</AllContext.Provider>
);
};
export default AllContextProvider;

TypeError: setEmail is not a function

I am having problem on my reactjs. I can't open login.
setEmail is not function and for setPassword is same. I tried to edit and checked my codes but still and error. what should I do?
I am already run it. but when I create my username there is an error.
here the error
here the codes for Login.js
import React from 'react';
const login = (props) => {
const { email, setEmail, password, setPassword, handleLogin, handleSignup, hasAccount, setHasAccount, emailError, passwordError} = props;
return (
<section className="login">
<div className="loginContainer">
<label htmlFor="">Username</label>
<input type="text"
autoFocus
required
value={email}
onChange={e => setEmail(e.target.value)}
/>
<p className="errorMsg">{ emailError }</p>
<label>Password</label>
<input
type="password"
requited
value={password}
onChange={e => setPassword(e.target.value)}
/>
here for App.js
App.js
import React, {useState, useEffect} from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
// import Login from '../Login';
import routes from "./routes";
import withTracker from "./withTracker";
import "bootstrap/dist/css/bootstrap.min.css";
import "./shards-dashboard/styles/shards-dashboards.1.1.0.min.css";
import './App.css';
import fireDb from './firebase/firebase';
import './App.css';
//Components
import Dashboard from "./views/Dashboard";
import Login from "./views/Login";
function App() {
//States
const [user, setUser] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [emailError, setEmailError] = useState('');
const [passwordError, setPasswordError] = useState('');
const [hasAccount, setHasAccount] = useState(false);
const clearInputs = () => {
setEmail('')
setPassword('')
}
const clearErrors = () => {
setEmailError('')
setPasswordError('')
}
//Login function
const handleLogin = () => {
clearErrors();
fireDb.auth()
.signInWithEmailAndPassword(email, password)
.catch(error => {
switch (error.code) {
case 'auth/invalid-email':
case 'auth/user-disabled':
case 'auth/user-not-found':
setEmailError(error.message);
break;
case 'auth/wrong-password':
setPasswordError(error.message);
break;
}
})
}
//Signup function
const handleSignup = () => {
clearErrors();
fireDb.auth()
.createUserWithEmailAndPassword(email, password)
.catch(error => {
switch (error.code) {
case 'auth/email-already-in-use':
case 'auth/invalid-email':
setEmailError(error.message);
break;
case 'auth/weak-password':
setPasswordError(error.message);
break;
}
})
}
//Function for logout
const handleLogout = () => {
fireDb.auth().signOut();
}
//Authentication listener
const authListener = () => {
fireDb.auth().onAuthStateChanged(user => {
if ( user ){
clearInputs();
setUser(user);
} else { setUser('') }
})
}
//React listener.
useEffect(() => {
authListener();
},[]);
return (
<div className="App">
{ user ? (
<Dashboard handleLogout={handleLogout} />
) : (
<Login
email={email}
setEmail={setEmail}
password={password}
setPassword={setPassword}
handleLogin={handleLogin}
handleSignup={handleSignup}
hasAccount={hasAccount}
setHasAccount={setHasAccount}
emailError={emailError}
passwordError={passwordError}
/>
)}
</div>
);
}
export default () => (
<Router basename={process.env.REACT_APP_BASENAME || ""}>
<div>
{/*
<Dashboard/> */}
{routes.map((route, index) => {
return (
<Route
key={index}
path={route.path}
exact={route.exact}
component={withTracker(props => {
return (
<route.layout {...props}>
<route.component {...props} />
</route.layout>
);
})}
/>
);
})}
</div>
</Router>
);
// export default App;
[the errors can not setEmail][2]
This issue is because your login component is loading directly without any props. You should load the login component through the App component to get all the props.
For that make the below changes in routes.js,
{
path: "/login",
layout: DefaultLayout,
component: App // export App and import here
},
Working Code - https://codesandbox.io/s/cranky-fog-efen8?file=/src/App.js

How to assign value onClick from different component to another component in React

What i want to do :
When i click my button i.e Search in Navbar.js i want to assign the search text in the variable urlQuery so i can pass it as props in Episodes.js component
End goal is to pass the urlQuery from Navbar.js somehow to Episodes.js component so i can query the REST api
How do i achieve the desired behaviour pls help
App.js
import React, { useState } from 'react';
import './App.css'
import Episodes from './components/Episodes/Episodes'
import CustomNavbar from './components/Navbar/Navbar'
import Pagination from './components/Pagination/Pagination'
function App() {
const [postsPerPage] = useState(20);
const [currentPage, setCurrentPage] = useState(1);
const url=`https://rickandmortyapi.com/api/episode?page=${currentPage}`
let urlQuery = `https://rickandmortyapi.com/api/episode?name=${SEARCH TEXT HERE}`
const paginate = pageNumber => setCurrentPage(pageNumber);
return (
<div>
<CustomNavbar />
<Episodes
urlQuery={urlQuery}
url={url}
/>
<Pagination
postsPerPage={postsPerPage}
totalPosts={36}
paginate={paginate}
/>
</div>
);
}
export default App;
Navbar.js
import React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import FormControl from 'react-bootstrap/FormControl';
const customNavbar = () => {
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">Rick And Morty</Navbar.Brand>
<Form inline>
<FormControl type="text" placeholder="Search" />
<Button>Search</Button>
</Form>
</Navbar>
);
}
export default customNavbar
Edit
On Zohaib's suggestion this error is thrown
Failed to compile.
./src/components/Navbar/Navbar.js
Line 14:48: Unexpected use of 'event' no-restricted-globals
Search for the keywords to learn more about each error.
App.js
import React, { useState, useEffect } from 'react';
import './App.css'
import Episodes from './components/Episodes/Episodes'
import CustomNavbar from './components/Navbar/Navbar'
import Pagination from './components/Pagination/Pagination'
function App() {
const [postsPerPage] = useState(20);
const [currentPage, setCurrentPage] = useState(1);
const [userSearchValue, setUserSearchValue] = useState('');
const [url, setUrl] = useState(``);
const [urlQuery, setUrlQuery] = useState(``)
useEffect(() => {
setUrl(`https://rickandmortyapi.com/api/episode?page=${currentPage}`)
}, [currentPage]);
useEffect(() => {
setUrlQuery(`https://rickandmortyapi.com/api/episode?name=${userSearchValue}`)
}, [userSearchValue])
const paginate = pageNumber => setCurrentPage(pageNumber);
const handleButtonClick = (searchValue) => {
setUserSearchValue(searchValue);
}
return (
<div>
<CustomNavbar
onButtonClick={handleButtonClick}
/>
<Episodes
urlQuery={urlQuery}
url={url}
/>
<Pagination
postsPerPage={postsPerPage}
totalPosts={36}
paginate={paginate}
/>
</div>
);
}
export default App;
Navbar.js
import React, { useState } from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import FormControl from 'react-bootstrap/FormControl';
const customNavbar = ({ onButtonClick }) => {
const [searchValue, setSearchValue] = useState('');
return (
<Navbar bg="light" expand="lg">
<Navbar.Brand href="#home">Rick And Morty</Navbar.Brand>
<Form inline>
<FormControl type="text" placeholder="Search" value={searchValue} onChange={(e) => setSearchValue(e.target.value)} />
<Button onClick={() => onButtonClick(searchValue)}>Search</Button>
</Form>
</Navbar>
);
}
export default customNavbar
The important part here is you're passing down the handleButtonClick function to the child component (Navbar). This way you can call that parent function in the child component whenever you want (ie. when the user clicks the submit button).
Do you mean something like this?
There is a React guide about this specific problem: Lifting State Up.
Normally what you do is you manage the state in the parent. In this case App where you manage the search text state. You pass down a function to components to change this state. The components that depend upon this state are passed the value through the properties.
Here is an example:
const {useEffect, useState} = React;
function App() {
const episodesURL = "https://rickandmortyapi.com/api/episode";
const [page, setPage] = useState(1);
const [pageInfo, setPageInfo] = useState({});
const [searchText, setSearchText] = useState("");
const [episodes, setEpisodes] = useState([]);
useEffect(() => {
const url = new URL(episodesURL);
url.searchParams.set("page", page);
if (searchText) url.searchParams.set("name", searchText);
fetch(url)
.then(response => response.json())
.then(response => {
if (response.error) {
setPageInfo({});
setEpisodes([]);
} else {
setPageInfo(response.info);
setEpisodes(response.results);
}
});
}, [page, searchText]);
const search = searchText => {
setSearchText(searchText);
setPage(1);
};
return (
<div>
<CustomNavbar search={search} />
<Episodes episodes={episodes} />
<Pagination setPage={setPage} info={pageInfo} />
</div>
);
}
function CustomNavbar({search}) {
const [searchText, setSearchText] = useState("");
const handleFormSubmit = event => {
event.preventDefault();
search(searchText);
};
return (
<form onSubmit={handleFormSubmit}>
<input
type="text"
placeholder="search"
value={searchText}
onChange={event => setSearchText(event.target.value)}
/>
<button type="submit">Search</button>
</form>
);
}
function Episodes({episodes}) {
return (
<table>
<thead>
<tr>
<th>episode</th>
<th>name</th>
<th>air date</th>
</tr>
</thead>
<tbody>
{episodes.map(episode => (
<tr key={episode.id}>
<td>{episode.episode}</td>
<td>{episode.name}</td>
<td>{episode.air_date}</td>
</tr>
))}
</tbody>
</table>
);
}
function Pagination({setPage, info}) {
return (
<div>
{info.prev && <a onClick={() => setPage(page => page - 1)}>previous</a>}
{info.next && <a onClick={() => setPage(page => page + 1)}>next</a>}
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
th { text-align: left; }
a { cursor: pointer; }
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Change urlQuery to state variable. Then, pass setUrlQuery to NavBar as a prop and on search button clickEvent call setUrlQuery function.

Render page with new data after change Context

I have a form on 'products/add' and I add products to the database, after I submit the request, I redirect to the page where all the products are displayed. However, this page does not display information about the last item I added. How to fix it? How do I render pages after redirects to display the most current data?
'localhost:3333/products/add'
import React, {useState} from 'react';
import api from './api';
import { Redirect } from 'react-router'
const HandleProduct = () => {
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [redirect, setRedirect] = useState(false);
const updateName = (e) =>{
setName(e.target.value);
}
const updateDescription = (e) =>{
setDescription(e.target.value);
}
const addProduct = (e) =>{
e.preventDefault();
const product = {
name: name,
description: description
}
api.addProduct(product)
.then((req, res) =>{
console.log(res);
setRedirect(true);
})
}
if(redirect) {
return <Redirect to={'/products'} />
}
return (
<div>
<form onSubmit={addProduct}>
<input type="text" name="name" value={name} onChange={updateName}/>
<input type="text" name="description" value={description} onChange={updateDescription}/>
<button>Submit</button>
</form>
</div>
);
}
export default HandleProduct;
List of products(localhost:3333/products):
import React, {useContext} from 'react';
import {ProductsContext} from './ProductsContext';
const ProductsList = () => {
const [data] = useContext(ProductsContext);
return (
<div>
{console.log(data)}
{data.products.map((product, index)=>(
<div key={index}>
<p>{product.name}</p>
<p><i>{product.description}</i></p>
</div>
))}
</div>
);
}
export default ProductsList;

how to get input field value on button click in react?

Could you please tell me how to get input field value on button click in react , I am using react hooks .I want to get first name and lastname value on button click. I already pass name attribute in my function component.
Here is my code
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
export default function InputField({name,label}) {
const [state, setState] = useState('')
return (
<div>
<label>{label}</label>
<input type="text"
value={state}
name={name}
onChange={(e) => setState(e.target.value)} />
{state}
</div>
);
}
Use <form> tag with useRef hook
Wrap your <InputField> tags with an html <form> tag and put a react ref on the later. Like this:
import React, { Component, useRef } from 'react'
import { render } from 'react-dom'
import InputField from './inputfield'
import './style.css'
function App () {
const nameForm = useRef(null)
const handleClickEvent = () => {
const form = nameForm.current
alert(`${form['firstname'].value} ${form['lastname'].value}`)
}
return (
<div>
<form ref={nameForm}>
<InputField label={'first name'} name={'firstname'}/>
<InputField label={'last name'} name={'lastname'}/>
</form>
<button onClick={handleClickEvent}>gett value</button>
</div>
)
}
render(<App />, document.getElementById('root'))
Working example: https://stackblitz.com/edit/react-shtnxj
The Easiest Way For Me is useRef
With useRef it's pretty simple. Just add ref name and then submit.
const email = useRef(null);
function submitForm(e){
e.preventDefault();
console.log(email.current.value);
}
return (
<div>
<form onSubmit={submitForm}>
<input type="text" ref={email} />
<button>Submit</button>
</form>
</div>
)
You could always lift up the state in parent component.
codeSandbox link
Parent Component
import React from "react";
import ReactDOM from "react-dom";
import ChildComponent from "./Child";
const { useState } = React;
function App() {
const [first_name, setFirstName] = useState("");
const [last_name, setLastName] = useState("");
const handleFirstNameChange = ({ target }) => {
setFirstName(target.value);
};
const handleLastNameChange = ({ target }) => {
setLastName(target.value);
};
const handleClick = () => {
console.log(first_name);
console.log(last_name);
};
return (
<div className="App">
<ChildComponent
label="first name"
onChange={handleFirstNameChange}
value={first_name}
/>
<ChildComponent
label="last name"
onChange={handleLastNameChange}
value={last_name}
/>
<button onClick={handleClick}>Click me</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Child Component
import React from "react";
const ChildComponent = ({ label, onChange, value, name }) => {
return (
<div>
<label>{label}</label>
<input type="text" value={value} name={name} onChange={onChange} />
</div>
);
};
export default ChildComponent;
You could always combine onChange handler for first name and last name.
Hope that helps!!!
A good solution is to move the state from InputField component into index:
const [F_name, setF_name] = useState('')
const [L_name, setL_name] = useState('')
now you should pass state value and event handler to InputField to change the state when input is changed:
<InputField label={'first name'} name={'firstname'} value={F_name} changed={(name) => setF_name(name)}/>
In Your InputField field: edit it to be like:
<input type="text"
value={value}
name={name}
onChange={(e) => changed(e.target.value)} />
See Working Demo Here
import React, { useRef } from 'react'
const ViewDetail = () => {
const textFirstName = useRef(null)
const onChange = e => {
console.log(textFirstName.current.state.value)
}
return <Input maxLength={30} ref={textFirstName} placeholder="Nombre" onChange=onChange} />
}
I can think of these approaches -
You can pull the state up to the parent component.
App.js
const [user, setUser] = useState('');
return (
<Inputfield setValue={setUser} value={user} />
);
InputField.js
<input value={props.value} onChange={(e) => setValue(e.target.value)} />
You can use ref to access indiviual element value.
If you have data distributed across multiple components you can also make use of Context API
Hope this helps!
Do let me know if you need more info on any of the option. Thanks!
You should do the react hooks work on your index and pass the value and the onChange function to your InputField component.
//index page
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
import InputField from './inputfield';
import './style.css';
function App() {
const [firstname, setFirstName] = useState('');
const [lastname, setLastName] = useState('');
const handleClickEvent = ()=>{
setFirstName('Will');
setLastName('smith');
}
return (
<div>
<InputField
label={'first name'}
name={'firstname'}
value={firstname}
onChange={setFirstName}
/>
<InputField
label={'last name'}
name={'lastname'}
value={lastname}
onChange={setLastName}
/>
<button
onClick={handleClickEvent}
>Get value</button>
</div>
);
}
render(<App />, document.getElementById('root'));
// input field
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
export default function InputField({name,label, value, onChange}) {
return (
<div>
<label>{label}</label>
<input type="text"
value={value}
name={name}
onChange={(e) => onChange(e.target.value)} />
{value}
</div>
);
}
While keeping the majority of your structure the same, I think the simplest and most React solution is to use forwardRef() which in a nut-shell let's us communicate between then parent-component and child-components.
See working sandbox.
App.js
import React, { useRef } from "react";
import InputField from "./InputField";
import ReactDOM from "react-dom";
function App() {
const handleClickEvent = () => {
if (firstName.current && lastName.current) {
console.log(`firstName: ${firstName.current.value}`);
console.log(`lastName: ${lastName.current.value}`);
}
};
const firstName = useRef(null);
const lastName = useRef(null);
return (
<div>
<InputField ref={firstName} label={"first name"} name={"firstname"} />
<InputField ref={lastName} label={"last name"} name={"lastname"} />
<button onClick={handleClickEvent}>Get value</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
InputField.js
import React, { useState } from "react";
const InputField = React.forwardRef((props, ref) => {
const [state, setState] = useState("");
return (
<div>
<label>{props.label}</label>
<input
ref={ref}
type="text"
value={state}
name={props.name}
onChange={e => setState(e.target.value)}
/>
{state}
</div>
);
});
export default InputField;
Notice that with this structure, you are not required to pass in any state updating function as props to the InputField component. The value that you enter into each input will be strictly maintained by the individual component. It is independent from the Parent, and therefore makes it much more reusable.
The refs we created allow us to tap into specific elements of the InputField so we extract the desired values. In this case, we can get first-name and last-name through the handleClickEvent function.
you can achieve this doing the following:
import React, { Component, useState } from 'react';
import { render } from 'react-dom';
export default function InputField({name,label}) {
const [state, setState] = useState('');
const handleChange = e => {
setState(e.target.value);
};
return (
<div>
<label>{label}</label>
<input
type="text"
value={state}
name={name}
onChange={handleChange}
/>
{state}
</div>
);
}
Hopes this helps.
well one simple(but not necessarily recommended) way is to provide an id or a ref like this in index.js
<InputField label={'first name'} name={'firstname'} id={"ip1"}/>
<InputField label={'last name'} name={'lastname'} id={"ip2"}/>
and in your inputfield.js pass the id props to the input fields like this
<input type="text"
value={state}
name={name}
onChange={(e) => setState(e.target.value)}
id= {id}/>
Now you can call them in the onClick of the button like this in index.js
const handleClickEvent = ()=>{
alert(document.getElementById("ip1").value);
}
The second, more preferable way is to set the state variable in index.js
function App() {
const [stateIp1, setStateIp1] = useState('');
const [stateIp2, setStateIp2] = useState('');
const handleClickEvent = ()=>{
alert(stateIp1);
}
return (
<div>
<InputField label={'first name'} state={stateIp1} setState={setStateIp1} name={'firstname'} id={"ip1"}/>
<InputField label={'last name'}state={stateIp2} setState={setStateIp2} name={'lastname'} id={"ip2"}/>
<button
onClick={handleClickEvent}
>Get value</button>
</div>
);
}
Now your inputfield.js becomes
export default function InputField({name,label,id,setState,state}) {
return (
<div>
<label>{label}</label>
<input type="text"
value={state}
name={name}
onChange={(e) => setState(e.target.value)} id= {id}/>
</div>
);

Categories