How to pass useState state to another component? - javascript

There are two components
WelcomePage.jsx
import { useState } from "react";
import SignUpPage from "./SignUpPage";
function WelcomePage() {
const [signUp, toSignUp] = useState(false);
function signUpClick() {
toSignUp(true);
}
return (
<div>
{signUp ? (
<SignUpPage isOpen={signUp} />
) : (
<div
className="Welcome_page__container animate__animated animate__fadeIn"
id="welcome_page"
>
<h1 className="Welcome_page__title">Welcome to Hotel Review </h1>
<h3 className="Welcome_page__subtitle">Sign in :</h3>
<div className="Welcome_page__wrapper">
<label className="Welcome_page__input-title" htmlFor="welcome_mail">
E-mail:
</label>
<input
className="Welcome_page__input"
id="welcome_mail"
type="mail"
placeholder="Your e-mail..."
/>
<label className="Welcome_page__input-title" htmlFor="welcome_pass">
Password:
</label>
<input
className="Welcome_page__input"
id="welcome_pass"
type="text"
placeholder="Your password..."
/>
<button className="Welcome_page__btn">Login</button>
<button className="Welcome_page__btn" onClick={signUpClick}>
Sign Up
</button>
</div>
</div>
)}
</div>
);
}
export default WelcomePage;
SignUpPage.jsx
function SignUpPage() {
return (
<div className="Welcome_page__container animate__animated animate__fadeIn">
<button>Back...</button>
<h1 className="Welcome_page__title">Welcome to Hotel Review </h1>
<h3 className="Welcome_page__subtitle">Sign up :</h3>
<div className="Welcome_page__wrapper">
<label className="Welcome_page__input-title" htmlFor="welcome_mail">
E-mail:
</label>
<input
className="Welcome_page__input"
id="welcome_mail"
type="mail"
placeholder="Your e-mail..."
/>
<label className="Welcome_page__input-title" htmlFor="welcome_pass">
Password:
</label>
<input
className="Welcome_page__input"
id="welcome_pass"
type="text"
placeholder="Your password..."
/>
<label
className="Welcome_page__input-title"
htmlFor="welcome_pass"
></label>
<input
className="Welcome_page__input"
id="welcome_pass_repeat"
type="text"
placeholder="Repeat password..."
/>
<button className="Welcome_page__btn_2">Sign Up</button>
</div>
</div>
);
}
export default SignUpPage;
Clicking the "Sign Up" button in WelcomePage.jsx using useState navigates to SignUpPage.jsx
Question - How can I return back to WelcomePage.jsx on the "Back" button (I understand that I need to return false back to const [signUp, toSignUp] = useState() , but I don't know how to transfer state from WelcomePage to SignUpPage and vice versa.)

You can pass it via props from parent to child component.
And pass the setState from child component to parent.
import { useState } from "react";
import SignUpPage from "./SignUpPage";
function WelcomePage() {
const [signUp, setSignUp] = useState(false);
function signUpClick() {
toSignUp(true);
}
return (
<>
{signUp ? (
<SignUpPage isOpen={signUp} setOpen={setSignUp} />
) : (
<div>
... component
</div>
)
</>
);
}
export default WelcomePage;
function SignUpPage(props) {
const { isOpen, setOpen } = props;
return (
<>
{isOpen}
<button onClick={setOpen(true)}>Open</button>
<button onClick={setOpen(false)}>Close</button>
</>
);
}
export default SignUpPage;

Related

Why is the entry in the state deleted?

I tried to do something very simple but I do not understand why it not working for me
so i have a component that include the details about the user are logged in
and i want to request the details user and put them in the component
so I do a axios.get to my back-end i get the detail set them in the state with success put them in the component but when I refresh the page i get state is undefined
I add here the code
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from 'react-router-dom'
import './account.css'
function Account(props) {
const [user, setUser] = useState()
const [currentPass, setCurrentPass] = useState()
const [newPass, setNewPass] = useState()
const [confirmPass, setConfirmPass] = useState()
let navigate = useNavigate();
useEffect(() => {
let userArr = []
try {
axios.create({ withCredentials: true }).get(`http://127.0.0.1:3000/users/getMe`)
.then(res => {
console.log(res.data.data.user) //object
userArr.push(res.data.data.user)
console.log(userArr)
setUser(userArr)
console.log(user)
})
} catch (error) {
console.log(error)
}
}, [])
const updatePassword = async (e) => {
e.preventDefault()
try {
const res = await axios.create({ withCredentials: true }).patch(`http://127.0.0.1:3000/users/updatePassword`, {
currentPassword: currentPass,
newPassword: newPass,
passwordConfirm: confirmPass
});
if (!res) {
return "not work"
}
console.log(res.data.data.user)
navigate("/", { replace: true });
} catch (error) {
console.log(error);
}
}
return (
<div className="account-container" >
<div className="title-container">
<p className="title">My Account</p>
</div>
<p className="sub-title">User information </p>
<form className="form-user-information">
<div className="div-form-user-information">
<label className="label-form-user-information">Username</label>
<textarea placeholder="Username..." />
</div>
<div className="div-form-user-information">
<label className="label-form-user-information">ssss</label>
<textarea placeholder={user.map(el => {
return el.email
})} />
</div>
<div className="div-form-user-information">
<label className="label-form-user-information">First name</label>
<textarea placeholder="First name..." />
</div>
<div className="div-form-user-information">
<label className="label-form-user-information">Last name</label>
<textarea placeholder="Last name..." />
</div>
</form>
<div className="buttom-line"></div>
{/* //////CONTACT INFORMATION////////// */}
<p className="sub-title-contact">CONTACT INFORMATION </p>
<form className="form-contact-information">
<div className="div-form-contact-information">
<label className="label-form-contact-information">Full Address</label>
<textarea placeholder="Full Address..." />
</div>
<div className="div-form-contact-information">
<label className="label-form-contact-information">City</label>
<textarea placeholder="City..." />
</div>
<div className="div-form-contact-information">
<label className="label-form-contact-information">Country</label>
<textarea placeholder="Country..." />
</div>
<div className="div-form-contact-information">
<label className="label-form-contact-information">Postal code</label>
<textarea placeholder="Postal code..." />
</div>
</form>
<div className="buttom-line-contact"></div>
{/* //////PASSWORD INFORMATION////////// */}
<p className="sub-title-password">UPDATE PASSWORD </p>
<form className="form-password-information">
<div className="div-form-password-information">
<label className="label-form-password-information">Current Password</label>
<textarea onChange={(e) => setCurrentPass(e.target.value)} placeholder="Current Password..." />
</div>
<div className="div-form-password-information">
<label className="label-form-password-information">New Password</label>
<textarea onChange={(e) => setNewPass(e.target.value)} placeholder="New Password..." />
</div>
<div className="div-form-password-information">
<label className="label-form-password-information">Confirm Password</label>
<textarea onChange={(e) => setConfirmPass(e.target.value)} placeholder="Confirm Password..." />
</div>
<button onClick={(e) => updatePassword(e)} className="btn-update-pass">Update password</button>
</form>
</div>
);
}
export default Account;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Based on your post and comment, the issue seems to be that you are trying to access the value of the user state property before it is populated by the async axios call.
This is a common occurrence in React, and is quite simple to solve via conditional rendering.
Example:
{user === undefined
? <></>
: <textarea placeholder={user.map(el => {
return el.email
})} />
}
Or, instead of an empty fragment, you can put a loader/spinner or any other UI you'd like, in-order to let the user know that something is loading.
If you have more places where you try to access the value of user, you can use the conditional rendering there as-well.
Alternativaly, like Matias mentioned, you can put the condition before the "main" return of the component.
This is useful if you have multiple places where you need to access the value of user and you don't want to copy this logic for all of them.
Example:
if (user === undefined) {
return <></>
}
return (
...
)
maybe the state is not filled when the app is rendered. Try waiting for the axios request by adding a little loading system:
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useParams, useNavigate } from 'react-router-dom'
import './account.css'
function Account(props) {
const [user, setUser] = useState()
const [currentPass, setCurrentPass] = useState()
const [newPass, setNewPass] = useState()
const [confirmPass, setConfirmPass] = useState()
const [loading, setLoading] = useState(true) // add this
let navigate = useNavigate();
useEffect(() => {
let userArr = []
try {
axios.create({ withCredentials: true }).get(`http://127.0.0.1:3000/users/getMe`)
.then(res => {
console.log(res.data.data.user) //object
userArr.push(res.data.data.user)
console.log(userArr)
setUser(userArr)
console.log(user)
setLoading(false) // and this
})
} catch (error) {
console.log(error)
}
}, [])
const updatePassword = async (e) => {
e.preventDefault()
try {
const res = await axios.create({ withCredentials: true }).patch(`http://127.0.0.1:3000/users/updatePassword`, {
currentPassword: currentPass,
newPassword: newPass,
passwordConfirm: confirmPass
});
if (!res) {
return "not work"
}
console.log(res.data.data.user)
navigate("/", { replace: true });
} catch (error) {
console.log(error);
}
}
if(loading) return <p>loading</p> //and this...
return (
<div className="account-container" >
<div className="title-container">
<p className="title">My Account</p>
</div>
<p className="sub-title">User information </p>
<form className="form-user-information">
<div className="div-form-user-information">
<label className="label-form-user-information">Username</label>
<textarea placeholder="Username..." />
</div>
<div className="div-form-user-information">
<label className="label-form-user-information">ssss</label>
<textarea placeholder={user.map(el => {
return el.email
})} />
</div>
<div className="div-form-user-information">
<label className="label-form-user-information">First name</label>
<textarea placeholder="First name..." />
</div>
<div className="div-form-user-information">
<label className="label-form-user-information">Last name</label>
<textarea placeholder="Last name..." />
</div>
</form>
<div className="buttom-line"></div>
{/* //////CONTACT INFORMATION////////// */}
<p className="sub-title-contact">CONTACT INFORMATION </p>
<form className="form-contact-information">
<div className="div-form-contact-information">
<label className="label-form-contact-information">Full Address</label>
<textarea placeholder="Full Address..." />
</div>
<div className="div-form-contact-information">
<label className="label-form-contact-information">City</label>
<textarea placeholder="City..." />
</div>
<div className="div-form-contact-information">
<label className="label-form-contact-information">Country</label>
<textarea placeholder="Country..." />
</div>
<div className="div-form-contact-information">
<label className="label-form-contact-information">Postal code</label>
<textarea placeholder="Postal code..." />
</div>
</form>
<div className="buttom-line-contact"></div>
{/* //////PASSWORD INFORMATION////////// */}
<p className="sub-title-password">UPDATE PASSWORD </p>
<form className="form-password-information">
<div className="div-form-password-information">
<label className="label-form-password-information">Current Password</label>
<textarea onChange={(e) => setCurrentPass(e.target.value)} placeholder="Current Password..." />
</div>
<div className="div-form-password-information">
<label className="label-form-password-information">New Password</label>
<textarea onChange={(e) => setNewPass(e.target.value)} placeholder="New Password..." />
</div>
<div className="div-form-password-information">
<label className="label-form-password-information">Confirm Password</label>
<textarea onChange={(e) => setConfirmPass(e.target.value)} placeholder="Confirm Password..." />
</div>
<button onClick={(e) => updatePassword(e)} className="btn-update-pass">Update password</button>
</form>
</div>
);
}
export default Account;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Uncaught TypeError: Cannot destructure property when using React context

Hello guys so i tried to make global state so the other page can use the state. The problem is i got an error that says:
Login.js:18 Uncaught TypeError: Cannot destructure property 'emailLog' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined.
Im doing this because i want the email from user after logged in and pass them to another page so that i can display the logged in user.
App.js:
export const EmailUser = React.createContext();
function App() {
Axios.defaults.withCredentials = true;
const [invoice, setInvoice] = useState("");
const [currency, setCurrency] = useState("IDR");
const [myFile, setMyFile] = useState("");
const [emailLog, setEmailLog] = useState("");
return (
<EmailUser.Provider value={{ emailLog, setEmailLog }}>
<div className="App">
<BasicExample />
<div className="formInput">
<form method="POST" encType="multipart/form-data" action="http://localhost:3001/upload">
<div className="textUser"></div>
<input
className="inputForm"
defaultValue={emailLog}
type="email"
disabled
name="email"
/>
<input className="inputForm" type="number" placeholder="Invoice No" name="InvoiceNo" />
<input className="inputForm" type="date" name="Invoice_Date" />
<input className="inputForm" type="text" placeholder="Description" name="Description" />
<select
className="selectBox"
name="Currency"
onChange={(e) => {
setCurrency(e.target.value);
}}
>
<option value="IDR">IDR</option>
<option value="USD">USD</option>
<option value="YEN">YEN</option>
</select>
<input className="inputForm" type="number" placeholder="Amount" name="Amount" />
<input
className="custom-file-upload"
multiple
type="file"
name="DocumentFile"
onChange={(e) => {
setMyFile(e.target.value);
}}
/>
<button className="btnSubmit">Submit</button>
</form>
</div>
</div>
</EmailUser.Provider>
);
}
export default App;
Login.js
const Login = () => {
let navigate = useNavigate();
const { emailLog, setEmailLog } = useContext(EmailUser);
const [passwordLog, setPasswordLog] = useState("");
const [loginStatus, setLoginStatus] = useState("");
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
email: emailLog,
password: passwordLog,
}).then((response) => {
console.log(response);
if (response.data.message) {
alert(response.data.message);
} else {
setLoginStatus(response.data[0].email);
alert("Redirecting");
navigate("/home");
}
});
};
console.log(emailLog);
useEffect(() => {
Axios.get("http://localhost:3001/login").then((response) => {
if (response.data.loggedIn == true) {
setLoginStatus(response.data.email[0].email);
}
});
});
return (
<div>
<img className="wave" src={Wave} />
<img className="wave2" src={WaveV2} />
<div className="wrapper">
<div className="img">{/* <img src={Background}/> */}</div>
<div className="register-content">
<div className="registerForm">
<img src={Avatar} />
<h2 className="title">Welcome</h2>
<div className="input-div one">
<div className="i">
<i className="fas fa-user">
<GrMail />
</i>
</div>
<div className="div">
<input
type="email"
className="input"
placeholder="Email"
required
onChange={(e) => {
setEmailLog(e.target.value);
}}
/>
</div>
</div>
<div className="input-div pass">
<div className="i">
<i className="fas fa-lock">
<AiFillLock />
</i>
</div>
<div className="div">
<input
type="password"
className="input"
placeholder="Password"
required
onChange={(e) => {
setPasswordLog(e.target.value);
}}
/>
</div>
</div>
Don't have an account ?
<button type="submit" className="btn" onClick={login}>
Login
</button>
</div>
</div>
</div>
</div>
);
};
export default Login;
EmailUser context works only with the components that are children of EmailUser.Provider, and it doesn't seem to be the case for Login component. An easy way is to create a separate component, in some EmailUserProvider.js, like so:
import {createContext, useState} from "react"
export const EmailUser = createContext();
export default function EmailUserProvider({children}) {
const [emailLog, setEmailLog] = useState("");
return (
<EmailUser.Provider value={{ emailLog, setEmailLog }}>
{children}
</EmailUser.Provider>
);
}
And make it wrap all the components that would consume it. If I assume all my components and routes are rendered in App and want the context to be global, I would do so:
<EmailUserProvider>
<App/>
</EmailUserProvider>

React setState hook keeps resetting and rendering the page twice

I have an react app Form component split into Login and Signup forms. It is supposed to render the Signup by default but switch to Login if login is button is clicked. When login button is clicked, the page switches to the Login form very briefly before switching back to the Signup form. I don't know what is causing this. I have tried placing const [page, setPage] = setState("signup") in the parent App and passing setPage as a prop along with page. This produced the same results. I believe this issue is similar to this one but that was not resolved.
Here is the app:
import Form from "./components/Signup-Form.js";
function App() {
return (
<div className="App">
<h1>Welcome</h1>
<Form />
</div>
);
}
export default App;
and Signup-Form.js:
import React from "react";
import { useState, useEffect } from "react";
import "./Forms.css";
import { InputField, Buttons } from "./Inputs";
function Form() {
const [page, setPage] = useState("signup");
const pageLabel = page;
let Signup = () => {
function toLogin() {
setPage("login");
}
return (
<form action="" method="get" className="form">
<div className="input-container">
<InputField name="Company Name" id="comp-name" type="text" />
<InputField name="Company ID" id="comp-id" type="text" />
<InputField name="Username" id="username" type="text" />
<InputField name="Email" id="email" type="email" />
<InputField name="Password" id="password" type="password" />
<InputField name="Confirm Password" id="confirm-password" type="password" />
</div>
<div className="btns">
<Buttons name="Sign Up" id="signup-btn" type="submit" cls="success" />
<Buttons name="Log In" id="login-btn" type="button" cls="success" alt="true" onClick={toLogin} />
</div>
</form>
);
};
let Login = () => {
function toSignup() {
setPage("signup");
}
return (
<form action="" method="get" className="form">
<div className="input-container">
<InputField name="Company ID" id="comp-id" type="text" />
<InputField name="Password" id="password" type="password" />
</div>
<div className="btns">
<Buttons name="Log In" id="login-btn" type="submit" cls="success" />
<Buttons name="Sign Up" id="signup-btn" type="submit" cls="success" alt onClick={toSignup} />
</div>
</form>
);
};
let form = (formType) => (
<div className="outer-wrapper">
<div className="form-wrapper">
<label className="form-title">{pageLabel}</label>
{formType}
</div>
</div>
);
if (page === "signup") {
const signup = Signup();
return form(signup);
} else if (page === "login") {
const login = Login();
return form(login);
}
}
export default Form;
The reason why after you click on Login button and get Login page and the page immediately re-renders and you get the Signup page is conditional render in your example. Right after clicking on Login button your state still previous and for this reason after click you get the main (Signup) page.
Suggest to change the structure render into smth like this:
...
return (
<div className="outer-wrapper">
<div className="form-wrapper">
<label className="form-title">{pageLabel}</label>
{page === "signup" && Signup()}
{page === "login" && Login()}
</div>
</div>
);
Here is the example in an action - https://codesandbox.io/s/smoosh-water-6jj18?file=/src/App.js

Strange behavior of inputs during pagination

For some reason, when switching to Registration, the 3rd input "inherits" the value from the previous state, that is, in theory, it should not have a value, but nevertheless an equal Login appears. How to fix?
import React, { useState } from 'react';
function Auth() {
const [pagination, setPagination] = useState('log');
function pag() {
if (pagination === 'log') {
return (
<>
<input
type="text"
className="email"
placeholder="E-mail"
/>
<input
type="text"
className="password"
placeholder="Пароль"
/>
<input
type="button"
className="done"
value="Войти"
/> // From here is transmitted
</>
);
}
return (
<>
<input
type="text"
className="email"
placeholder="E-mail"
/>
<input
type="text"
className="password"
placeholder="Пароль"
/>
<input
type="text"
className="password password-repeat"
placeholder="Повторите пароль"
/> // To here
<input
type="button"
className="done"
value="Зарегестрироваться"
/>
</>
);
}
function changePag(ev) {
if (ev.target.dataset.type === 'log') {
setPagination('log');
} else {
setPagination('reg');
}
}
return (
<div className="auth_page">
<div className="pagination">
<input
type="button"
className="log"
data-type="log"
onClick={changePag}
value="Вход"
/>
<input
type="button"
className="reg"
data-type="reg"
onClick={changePag}
value="Регистрация"
/>
</div>
<div className="form">
{pag()}
</div>
</div>
);
}
export default Auth;

Redux form defaultValue

How to set defaultValue to input component?
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
I tried like above but my fields are empty. I'm trying to create fieldArray (dynamic forms):
{fields.map((prize, index) =>
<div key={index} className="fieldArray-container relative border-bottom" style={{paddingTop: 35}}>
<small className="fieldArray-title marginBottom20">Prize {index + 1}
<button
type="button"
title="Remove prize"
className="btn btn-link absolute-link right"
onClick={() => fields.remove(index)}>Delete</button>
</small>
<div className="row">
<div className="col-xs-12 col-sm-6">
<Field name={`${prize}.rank`} defaultValue={index} component={Input} type='text'/>
<Field name={`${prize}.prizeId`} defaultValue={index} component={Input} type='text'/>
<Field
name={`${prize}.name`}
type="text"
component={Input}
label='Prize Name'/>
</div>
<div className="col-xs-12 col-sm-6">
<Field
name={`${prize}.url`}
type="text"
component={Input}
label="Prize URL"/>
</div>
<div className="col-xs-12">
<Field
name={`${prize}.description`}
type="text"
component={Input}
label="Prize Description" />
</div>
</div>
</div>
)}
On redux forms you can call initialize() with an object of values like so:
class MyForm extends Component {
componentWillMount () {
this.props.initialize({ name: 'your name' });
}
//if your data can be updated
componentWillReceiveProps (nextProps) {
if (/* nextProps changed in a way to reset default values */) {
this.props.destroy();
this.props.initialize({…});
}
}
render () {
return (
<form>
<Field name="name" component="…" />
</form>
);
}
}
export default reduxForm({})(MyForm);
This way you can update the default values over and over again, but if you just need to do it at the first time you can:
export default reduxForm({values: {…}})(MyForm);
This jsfiddle has an example
https://jsfiddle.net/bmv437/75rh036o/
const renderMembers = ({ fields }) => (
<div>
<h2>
Members
</h2>
<button onClick={() => fields.push({})}>
add
</button>
<br />
{fields.map((field, idx) => (
<div className="member" key={idx}>
First Name
<Field name={`${field}.firstName`} component="input" type="text" />
<br />
Last Name
<Field name={`${field}.lastName`} component="input" type="text" />
<br />
<button onClick={() => fields.remove(idx)}>
remove
</button>
<br />
</div>
))}
</div>
);
const Form = () => (
<FieldArray name="members" component={renderMembers} />
);
const MyForm = reduxForm({
form: "foo",
initialValues: {
members: [{
firstName: "myFirstName"
}]
}
})(Form);
this is my implementation using a HoC
import { Component } from 'react'
import {
change,
} from 'redux-form'
class ReduxFormInputContainer extends Component{
componentDidMount(){
const {
initialValue,
meta,
} = this.props
if(initialValue === undefined || meta.initial !== undefined || meta.dirty) return
const {
meta: { form, dispatch },
input: { name },
} = this.props
dispatch(change(form, name, initialValue))
}
render(){
const {
initialValue,
component: Compo,
...fieldProps
} = this.props
return <Compo {...fieldProps} />
}
}
function reduxFormInputContainer(component){
return function(props){
return <ReduxFormInputContainer {...props} component={component} />
}
}
export default reduxFormInputContainer
and then for exemple:
import reduxFormInputContainer from 'app/lib/reduxFormInputContainer'
InputNumericWidget = reduxFormInputContainer(InputNumericWidget)
class InputNumeric extends Component{
render(){
const props = this.props
return (
<Field component={InputNumericWidget} {...props} />
)
}
}

Categories