I am trying to pass some props from React class to my functional component which is formik and then I want to add some callback function to get those data back on text changed. But not sure how can i make this done. Please check my below code:
Here is my Main.jsx class
// Some imports were removed to keep everything looks cleaner
import RegisterAccount from "RegisterAccount.jsx";
class Main extends React.Compoenent {
constructor(props) {
super(props);
this.state = {
username: "This is username value..."
}
}
render() {
return (
<Container fluid>
<Switch>
<Route exact path="/register" component={RegisterAccount} data={this.state.username} />
</Switch>
</Container>
)
}
}
export default Main;
Here is my RegisterAccount.jsx
// Some imports were removed to keep everything looks cleaner
import { Form as FormikForm, Field, withFormik } from "formik";
import * as Yup from "yup";
const App = ({ values, errors, touched }) => (
<FormikForm className="register-form " action="">
<h3 className="register-title">Register</h3>
<Form.Group className="register-form-group">
<Field
name="username"
type="text"
placeholder="USERNAME"
className="form-control rounded register-form-control"
/>
{touched.username && errors.username && <p>{errors.username}</p>}
</Form.Group>
</FormikForm>
);
const FormikApp = withFormik({
mapPropsToValues({ username, email, password, confirmPassword }) {
return {
username: username || ""
};
},
validationSchema: Yup.object().shape({
username: Yup.string()
.min(6)
.max(32)
.required()
.test("username", "error message of username", async value => {
return true;
})
})(App);
export default FormikApp;
It looks like the issue you're seeing isn't from Formik, but from React Router. Your Route won't pass props in as data the way you have it:
<Route exact path="/register" component={RegisterAccount} data={this.state.username} />
Instead, you have to use Route's render prop and pass props directly into the component. This should pass username into mapPropsToValues:
<Route exact path="/" render={() => <RegisterAccount username={this.state.username} />} />
Related
I am now working on routing after login in successfully. But got this error:
Uncaught TypeError: Cannot read properties of undefined (reading 'navigate')
Here is the login page
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
loginName: "",
password: "",
loginNameError: null,
passwordError: null,
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.props.navigation.navigate("/employee")
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="input-container">
<label>User ID</label>
<input type="text" name="loginName" value={this.state.loginName} onChange={(e) => (this.setState({ loginName: e.target.value }))} />
<p>{this.state.loginNameError}</p>
</div>
<div className="input-container">
<label>Password</label>
<input type="password" name="password" value={this.state.password} onChange={(e) => (this.setState({ password: e.target.value }))} />
<p>{this.state.passwordError}</p>
</div>
<div className="button-container"><input type="submit" value="CONNECT"></input></div>
</form>
);
}
}
export default Login;
Here is the APP.js
function App() {
return (
<div class="page">
<BrowserRouter>
<Routes>
<Route path="/employee" element={<UserManagement />}></Route>
<Route path="/login" element={<Login />}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
how to solve this error?
Since react router v6 doesnt have withRouter
// https://reactrouter.com/docs/en/v6/getting-started/faq#what-happened-to-withrouter-i-need-it
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
// then in the Login class component you can consume withRouter
handleSubmit(event) {
event.preventDefault();
// consume `router` prop
this.props.router.navigate("/employee");
}
// Wrap Login in withRouter HOC to make sure `router` prop is available
export default withRouter(Login);
// export default Login;
This said, I would recommend using react-router with a function component, not a class component.
Because using of class component you can not use hooks!
but I think this work here:
this.props.history.push("/employee");
I just added a sign in page and component to an example react app I'm working on, but any time I navigate to the new page (localhost:3000/signin), the page hangs and looking into the Chrome task manager, starts using 300+% CPU power. Nothing is printed to the console, and nothing ever shows up on the screen.
I know this can happen when there is some sort of call loop that tries to render infinitely, but I don't see where that could be happening in the code I have.
Here is my App.js:
import React from "react";
import { Switch, Route } from "react-router-dom";
import "./App.css";
import HomePage from "./pages/homepage/homepage";
import ShopPage from "./pages/shop/shopPage";
import Header from "./components/header/header";
import SignInAndSignUpPage from "./pages/signInSignUp/signInSignUp";
function App() {
return (
<div>
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/shop" component={ShopPage} />
<Route path="/signin" component={SignInAndSignUpPage} />
</Switch>
</div>
);
}
export default App;
my SignIn/SignUp page (sign up not implemented yet):
import React from "react";
import SignIn from "../signInSignUp/signInSignUp";
import "./signInSignUp.scss";
const SignInAndSignUpPage = () => (
<div className="sign-in-and-sign-up">
<SignIn />
</div>
);
export default SignInAndSignUpPage;
and my SignIn component:
import React from "react";
import "./sign-in.styles.scss";
class SignIn extends React.Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
}
handleSubmit = event => {
event.preventDefault();
this.setState({ email: "", password: "" });
};
handleChange = event => {
const { value, name } = event.target;
this.setState({ [name]: value });
};
render() {
return (
<div className="sign-in">
<h2>I already have an account</h2>
<span>Sign in with your email and password</span>
<form onSubmit={this.handleSubmit}>
<input
name="email"
type="email"
onChange={this.handleChange}
value={this.state.email}
label="email"
required
/>
<input
name="password"
type="password"
value={this.state.password}
onChange={this.handleChange}
label="password"
required
/>
<input type="submit"> Sign in </input>
</form>
</div>
);
}
}
export default SignIn;
Thanks in advance.
I discovered that this issue was caused by a recursive import in another file. The component was importing itself by mistake, which caused infinite re-rendering. Always double-check your imports!
I am trying to send my dob data from my Main class to a child component (RegisterAccount.jsx) and validate it at child component using yup and withFormik Field. The problem is that:
I could not make yup or formik/yup to validate dob field.
DatePicker doesn't show selected value inside the textbox after selected.
Please check my below code:
Here is my Main.jsx class
// Some imports were removed to keep everything looks cleaner
import RegisterAccount from "RegisterAccount.jsx";
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
dob: ""
}
}
render() {
return (
<Container fluid>
<Switch>
<Route exact path="/" render={() => <RegisterAccount data={this.state.dob} />} />
</Switch>
</Container>
)
}
}
export default Main;
Here is my RegisterAccount.jsx
// Some imports were removed to keep everything looks cleaner
import { Form as FormikForm, Field, withFormik } from "formik";
import * as Yup from "yup";
import DatePicker from "react-datepicker";
const App = ({ props }) => (
<FormikForm className="register-form " action="">
<h3 className="register-title">Register</h3>
<Form.Group className="register-form-group">
<DatePicker
tag={Field}
selected={props.data.dob}
onChange={(e, val) => {
console.log(this);
this.value=e;
props.data.dob = e;
console.log(props.data.dob);
}}
value="01-01-2019"
className="w-100"
placeholderText="BIRTH DATE"
name="dob" />
{touched.username && errors.username && <p>{errors.username}</p>}
</Form.Group>
</FormikForm>
);
const FormikApp = withFormik({
mapPropsToValues({ data }) {
return {
dob: data.dob || ""
};
},
validationSchema: Yup.object().shape({
dob: Yup.date()
.max(new Date())
})(App);
export default FormikApp;
Use setFieldValue method from formik's injected props.
Define it on onChange handler for your inputsetFieldValue('dob','Your Value').
You can access it from
const MyForm = props => {
const {
values,
touched,
errors,
handleChange,
handleBlur,
handleSubmit,
setFieldValue
} = props;
return (
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={e => {
setFieldValue("name", "Your value"); // Access it from props
}}
onBlur={handleBlur}
value={values.name}
name="name"
/>
</form>
);
};
const MyEnhancedForm = withFormik({
// your code
})(MyForm)
Reference - https://jaredpalmer.com/formik/docs/api/formik#setfieldvalue-field-string-value-any-shouldvalidate-boolean-void
I have a question regarding lifting state up, I am aware that his is a frequently asked question on here but I hope someone will try to help me understand.
Problem: I have a login component consisting of a form component in which I do my fetch call to execute the login. I want to keep the token I get from my fetch call and save it in the App component so I can use this token and pass it down other pages/components.
So to begin with, I start with my code in my App.js / the parent component?
In here i manage my Routes, and this is also her that I want to store my state.
I pass down the token from this state to the Login component as seen on the Route that renders the Login component.
From what I read, props are not mutable so this actually doesn't work the way I thought out? I made the handleToken function but I will return to that.
export default class App extends Component {
state = {
token: "",
regNo: "",
};
handleToken = (token) => {
this.setState({ token: token})
}
render() {
console.log(this.state.token);
return (
<Router >
<div>
<Header />
<Navbar isLoggedIn={this.state.token} />
<Switch>
<Route exact path="/" render={() => <Home />} />
<Route path="/profile" render={() => <Profile userToken={this.state.token} />} />
<Route path="/login" render={() => <Login userToken={this.state.token} />} />
<Route path="/register" render={() => <Register />} />
<Route path="/carpage" render={() => <CarPage regNo={this.state.regNo} />} />
<Route path="/find-rental" render={() => <FindRental regNo={this.state.regNo} setRegno={this.setRegno}/>} />
<Route path="/rent-out-car" render={() => <RentOutCar />} />
<Route component={NoMatch} />
</Switch>
</div>
</Router >
);
};
Now we move down to the Login component. This component consist of multiple other components that makes up the login page. One of which is the FormContainer where the fetch call for the Login happens.
const Login = (props) => (
<Container fluid={true}>
<BrandRow>
</BrandRow>
<FormRow>
<FormContainer /* userToken={this.props.userToken} */ />
</FormRow>
</Container>
);
export default Login;
(Not sure why it wouldn't render it properly as a code sample, so it went into code snippet)
So as you can see in this Login component, I have the FormContainer which it were all the action happens.
Let me show you:
class FormContainer extends Component {
state = {
userName: '',
password: '',
};
handleChange = event => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({ [name]: value });
}
handleSubmit = event => {
event.preventDefault();
const credentials = { username: this.state.userName, password: this.state.password };
this.login(credentials);
}
login = credentials => {
const url = "https://fenonline.dk/SYS_Backend/api/login";
const postHeader = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(credentials)
};
fetch(url, postHeader).then(res => {
if (!res.ok) { throw Error(res.status + ": " + res.statusText + " | Wrong username or password!"); }
return res.json();
}).then(data => {
this.props.userToken = data.token;
alert("You have succesfully logged in!" + data.token);
this.props.history.push('/profile')
}).catch(error => alert(error));
}
render() {
const { userName, password } = this.state;
return (
<Container>
<Form onSubmit={this.handleSubmit}>
<Title>Sign In</Title>
<Input
type="text"
name="userName"
value={userName}
onChange={this.handleChange}
placeholder="Username.."
/>
<Input
type="password"
name="password"
value={password}
onChange={this.handleChange}
placeholder="Password.."
/>
<Button type="submit">Sign In</Button>
<Button onClick={() => this.props.history.push('/register')}>Register</Button>
</Form>
<Text>Rental service created by users for users.</Text>
</Container>
);
}
}
export default withRouter(FormContainer);
What you want to look at here is the login function, where I created the fetch call and this is where i get the token that i want to my lifted up state to store. Now i try and set it by saying this.props.userToken = data.token; but if props are immuteable how do I do this?
In order to lift state to your top level component, you need to pass your handleToken function into your login component as a prop.
<Route path="/login" render={() => <Login userToken={this.state.token} handleToken={this.handleToken} />} />
Then you will need to pass that into your FormContainer component as a prop as well.
<FormContainer handleToken={this.props.handleToken} />
Lastly, in your fetch, you'll need to call this method in the second .then() instead of trying to assign the token to a prop.
this.props.handleToken(data.token)
This will allow the state to be lifted up to the parent and then allow the token to be passed as a prop to your other components.
In my code I have a few checks after a user has entered some data, then I want to load the next route if everything is correct, what is the best way to do so?
This is my current Route page:
<Router history = {browserHistory}>
<Route exact path="/" component={() => <MainMenu userData={this.state.userData}/>}/>
<Route exact path="/login" component = {Login} />
<Route exact path="/pastMeetingsPlay/:meetingCode" component={(props) => <PastMeetingsPlay user={this.state.userData.UserID} {...props}/>} />
<Route exact path="/meetingMode/:meetingCode" component={(props) => <MeetingMode user={this.state.userData.UserID} {...props}/>} />
</Router>
the user submits a form then there inputs are checked and if all the required checks pass then it should load meetingMode page
EDIT:
import React, { Component } from 'react';
import './App.css';
import MeetingMode from'./MeetingMode';
import NavbarMenu from './Navbar';
import Popup from "reactjs-popup";
import axios from 'axios';
import {withRouter, history, Redirect, Route} from "react-router";
class MeetingModeLoad extends Component{
constructor(props)
{
super(props);
this.state ={
meeting:{},
value:0
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
async handleSubmit(event)
{
event.preventDefault();
let meetingLoadCode = this.state.value
try{
let getter = await axios.get(`https://smartnote1.azurewebsites.net/api/meetings/${meetingLoadCode}`)
let meetingLocal = getter.data
this.setState({meeting:meetingLocal})
if(meetingLocal.Status == 2)
{
console.log("please join meeting that is planned or under going")
}
else
{
console.log("/meetingMode/" + this.state.meeting.MeetingID);
this.props.history.push("/meetingMode/" + this.state.meeting.MeetingID)
}
}
catch(error)
{
console.error(error)
}
}
handleChange(event)
{
this.state.value = event.target.value
console.log(this.state.value)
}
render()
{
return(
<div>
<Popup
trigger={<button className="meetingModeButton" onClick={() => this.handleClick}>Meeting Mode</button>}
modal
closeOnDocumentClick>
<div className="newNote">
<header style={{background: "#F7941D" }}> Meeting Mode</header>
<form onSubmit={this.handleSubmit}>
<label> Enter Meeting Code :
<input type="text" name="type" className="inputBox" onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
{console.log(this.state.meeting)}
</Popup>
</div>
)
}
}
export default withRouter (MeetingModeLoad)
Looks like you forgot to wrap your component into withRouter. It is mandatory to access the history prop
Place this in the component from which you try to push:
import { withRouter } from 'react-router'
...
export default withRouter(YourComponent);
And push by using this in your component:
this.props.history.push("/meetingMode/" + meetingCode);