React Router v6 Button onclick Navigate vis class components - reactjs

Trying to port an application from router v2 to router v6.
Everything I see everywhere is hooks.
Tried to make a custom withRouter, "Hooks can only be called inside of the body of a function component."
The button in question
<YAButton
onClick={this.goToForgotPassword.bind(this)}
size="tiny"
style={YALogin.styles.forgotPassword}
type="text"
>
the function it calls
goToForgotPassword() {
this.props.navigate(`/forgot${isEmail(this.state.emailAddress) ?
`?emailAddress=${encodeURIComponent(this.state.emailAddress)}` :
''
}`);
}
the export at the bottom of the jsx file, perhaps something is amiss here?
export default connect(YALogin.mapStateToProps, YALogin.mapDispatchToProps)(Radium(withRouter(YALogin)));
and my withRouter
export const withRouter = (Component) => {
const Wrapper = (props) => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Component
{...props}
navigate={navigate}
location={location}
params={params}
/>
);
};
return Wrapper;
};
Clearly I can't use hooks, but I also can't change the entire application over to function based components.
How do click "Forgot Password" button to go the
`/forgot${isEmail(this.state.emailAddress) ?
`?emailAddress=${encodeURIComponent(this.state.emailAddress)}` :
''
}`
edit: The render().
render() {
return (
<main
className="splash"
style={{
backgroundImage: `url(${window.__configuration.cdn}/splash/splash.jpg)`
}}
>
<form
className="stripped"
onSubmit={this.sendLoginRequest.bind(this)}
>
{/* Render the application logo */}
<div className="dual-logo">
<img
src={`${window.__configuration.cdn}/logos/logo-redacted-y-negative.svg`}
className="redacted-logo"
alt="Weemarkable"
/>
<div className="logo-divider"></div>
<img
src={`${window.__configuration.cdn}/logos/logo-weemarkable-negative-${Translator.getLang()}.svg`}
className="weemarkable-logo"
alt="Weemarkable"
/>
</div>
{/* Set inner html to allow non-breaking-space tags to render */}
<p className="tagline"
dangerouslySetInnerHTML={{__html: Translator.translate("General.Tagline")}}></p>
<fieldset>
{/* Render an alert if the login has failed for any reason */}
{
this.props.loginError !== null ?
<YAAlert type="E">
<span>{this.state.errorMessage}</span>
</YAAlert> :
null
}
{/* Collect the email address from the user */}
<YAInput
type="email"
className="stripped-input"
noBaseStyles
onChange={this.onChange.bind(this, 'emailAddress')}
placeholder={Translator.translate("General.YourEmail")}
value={this.state.emailAddress}
/>
{/* Collect the password from the user */}
<YAInput
type="password"
className="stripped-input"
noBaseStyles
onChange={this.onChange.bind(this, 'password')}
placeholder={Translator.translate("Password.Password")}
value={this.state.password}
/>
<div style={YALogin.styles.controls}>
{/* Button which submits the login request to the server */}
<div>
<YAInput
type="submit"
className={classNames('btn-stripped', {disabled: !this.isFormValid()})}
noBaseStyles
value={Translator.translate("MainPage.SignIn")}
/>
</div>
{/* Button which takes the user to the forgot password screen */}
<div>
<YAButton
onClick={() => useNavigate()(`/forgot${isEmail(this.state.emailAddress) ? `?emailAddress=${encodeURIComponent(this.state.emailAddress)}` : ''}`)}
size="tiny"
style={YALogin.styles.forgotPassword}
type="text"
>
{Translator.translate("MainPage.ForgotPassword")}
</YAButton>
|
<YAButton
onClick={this.goToForgotPassword.bind(this)}
size="tiny"
style={YALogin.styles.forgotPassword}
type="text"
>
{Translator.translate("MainPage.TOU")}
</YAButton>
</div>
</div>
</fieldset>
</form>
{/* Render footer logo */}
<footer className="brand-bar login">
<img
src={`${window.__configuration.cdn}/logos/logo-redacted-of-hamilton-${window.__configuration.langCode}.svg`}
className="footer-logo"
alt="redactedof redacted| redacted| Brantford"
/>
</footer>
</main>
);
}
}

Related

How to navigate to another page in reactJS using SubmitHandler?

I wan to navigate to the dashboard page immediately after I submit the details. Here this is my CreateMonthWallet class where I am creating different for different months. Inside this create form all the information are there which are needed to be collected by the user. Once the user click the create button, the user should navigate back to the Dashboard page. Below given is the code of CreateMonthWallet class. When I run the code, once after clicking the cleate button it gives me the message of error but the data is updated to database but still its showing the message of error on the localhost page and doesn't navigating to the dashboard.
import axios from 'axios'
import React, { Component } from 'react'
class CreateMonthWallet extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
accountNumber: '',
description: '',
priority: ''
}
}
changeHandler = (event, fieldName) => {
this.setState({
[fieldName]: event.target.value
})
}
submitHandler = (event) => {
const newWallet = {
name: this.state.name,
accountNumber: this.state.accountNumber,
description: this.state.description,
priority: this.state.priority
}
axios.post('http://localhost:8080/monthwallet', newWallet)
.then((res) => {
this.props.history.push('/Dashboard')
}).catch((err) => {
alert("Error")
})
event.preventDefault()
}
render() {
return (
<div className="project">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h5 className="display-4 text-center">Create Wallet</h5>
<hr />
<form onSubmit={(event)=>this.submitHandler(event)}>
<div className="form-group">
<input type="text" onChange={(event) => this.changeHandler(event, "name")} className="form-control form-control-lg " placeholder="Account Name" />
</div>
<div className="form-group">
<input type="text" onChange={(event) => this.changeHandler(event, "accountNumber")} className="form-control form-control-lg" placeholder="Account No" />
</div>
<div className="form-group">
<textarea onChange={(event) => this.changeHandler(event, "description")} className="form-control form-control-lg" placeholder="Description"></textarea>
</div>
<div className="form-group">
<select className="form-control form-control-lg" onChange={(event) => this.changeHandler(event, "priority")}>
<option value={3}>Display Priority</option>
<option value={1}>High</option>
<option value={2}>Medium</option>
<option value={3}>Low</option>
</select>
</div>
<input type="submit" className="btn btn-dark btn-block mt-4" value="Create" />
</form>
</div>
</div>
</div>
</div>
)
}
}
export default CreateMonthWallet
Edit:
Adding the content of function App to the question. As it became relevant.
function App() {
return (
<Router>
<div>
<Nav />
<Routes>
<Route path="/" element={<Welcome />} exact />
<Route path="/Dashboard" element={<Dashboard />} exact />
<Route path="/CreateMonthWallet" element={<CreateMonthWallet />} exact />
<Route path="*" element={<NotFound />} exact />
</Routes>
</div>
</Router>
)
}
Issue
react-router-dom#6 removed the route props, so there should be some sort of error in the console along the lines of can't access property "push" of undefined when trying to execute this.props.history.push('/Dashboard'). The history object was replaced by a navigate function in RRDv6, accessible via the useNavigate hook.
Solution
You can either convert CreateMonthWallet into a React function component so it can use React hooks, or you can create a custom withRouter Higher Order component to inject the navigate function as a prop.
Convert to React function component
import axios from 'axios'
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
const CreateMonthWallet = () => {
const navigate = useNavigate();
const [state, setState] = useState({
name: '',
accountNumber: '',
description: '',
priority: ''
});
const changeHandler = (event) => {
const { name, value } = event.target;
setState(state => ({
...state,
[name]: value
}))
}
const submitHandler = (event) => {
event.preventDefault();
axios.post('http://localhost:8080/monthwallet', state)
.then((res) => {
navigate('/Dashboard');
})
.catch((err) => {
alert("Error");
});
}
return (
<div className="project">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h5 className="display-4 text-center">Create Wallet</h5>
<hr />
<form onSubmit={submitHandler}>
<div className="form-group">
<input
type="text"
name="name"
onChange={changeHandler}
className="form-control form-control-lg"
placeholder="Account Name"
value={state.name}
/>
</div>
<div className="form-group">
<input
type="text"
name="accountNumber"
onChange={changeHandler}
className="form-control form-control-lg"
placeholder="Account No"
value={state.accountNumber}
/>
</div>
<div className="form-group">
<textarea
name="description"
onChange={changeHandler}
className="form-control form-control-lg"
placeholder="Description"
value={state.description}
/>
</div>
<div className="form-group">
<select
className="form-control form-control-lg"
name="priority"
onChange={changeHandler}
value={state.priority}
>
<option value={3}>Display Priority</option>
<option value={1}>High</option>
<option value={2}>Medium</option>
<option value={3}>Low</option>
</select>
</div>
<input
type="submit"
className="btn btn-dark btn-block mt-4" value="Create"
/>
</form>
</div>
</div>
</div>
</div>
);
};
export default CreateMonthWallet;
Create withRouter HOC
Follow the instructions at What happened to withRouter? I need it! part of the RRDv6 FAQ to create a replacement withRouter HOC.
Example:
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
const withRouter = Component => props => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
Decorate the CreateMonthWallet with the new withRouter HOC and access this.props.router.navigate.
class CreateMonthWallet extends Component {
...
submitHandler = (event) => {
event.preventDefault();
const newWallet = { ...this.state };
axios.post('http://localhost:8080/monthwallet', newWallet)
.then((res) => {
this.props.router.navigate('/Dashboard');
})
.catch((err) => {
alert("Error")
});
}
render() {
return (
...
);
}
}
export default withRouter(CreateMonthWallet);
Looks like you did not create a history variable yet. It's a piece of cake.
This variable that you are going to create, named history, will be responsible for storing the history of your browsing within your app.
With history you earn the ability to goBack(), for example. Notice that react-router is now favoring navigate instead. You can have a look later at the link below later for upgrading to newer versions.
react-router go back a page how do you configure history?
But let's keep ourselves in the problem here.
import {BrowserRouter, } from "react-router-dom";
import { createBrowserHistory } from 'history';
function App() {
const history = createBrowserHistory() //creates the variable history
return (
<BrowserRouter>
<div>
<Nav />
<Routes>
<Route path="/" element={<Welcome />} exact />
<Route path="/dashboard" element={<Dashboard history={history}/>} exact /> //passes it down to child component
<Route path="/createmonthwallet" element={<CreateMonthWallet history={history}/>} exact /> //passes it down to child component
<Route path="*" element={<NotFound />} exact />
</Routes>
</div>
</BrowserRouter>
)
}
What we did here:
Create a history object by importing createBrowserHistory from history. Use 'npm install history' if you do not have it yet.
Defining the variable inside your App function.
Pass it down as 'props' in your Route.
Now, your component will have access to this.props.history and you the error should go away.
As sidenotes, prefer using function components anywere in your code. Much easier to read, write, and much more flexible. Also, avoid using uppercase letters in path. something like path="wallet/month/create" would look more elegant in the long term cause you could have "wallet/month/update", "wallet/month/delete" and so on

Show hide multiple password in react js

I'm currently learning React js. My code work when it has one show hide password. But when i have more than one, i have struggle. This code work, because it has just one.
export default function App() {
const [changePassword, setChangePassword] = useState(true);
const changeIcon = changePassword === true ? false : true;
return (
<div className="wrapper-login">
<div className="wrapper-form">
<h2>Welcome Back!</h2>
<form>
<label>Email</label>
<div>
<input
type="email"
name="email"
required
/>
</div>
<label>Password</label>
<div className="form-group">
<input
type={changePassword ? "password" : "text"}
name="password"
required
/>
<span className="icon"
onClick={() => {
setChangePassword(changeIcon);
}}
>
{changeIcon ? <EyeOutlined /> : <EyeInvisibleOutlined />}
</span>
</div>
</form>
</div>
</div>
);
}
In codesandbox i have 3 input type password, and each input have show hide password. Can you help me to achieved that ? and explain to me why thats work ? . I'm sorry for my bad English. Thank you
You may create a component that controls hide or show behavior. For example, you can create a generic component for isolated show hide behavior. You pass the input name, and it creates a sub-component for you.
export default function ShowHidePassword({ name }) {
const [isVisible, setVisible] = useState(false);
const toggle = () => {
setVisible(!isVisible);
};
return (
<div className="form-group">
<input type={!isVisible ? "password" : "text"} name={name} required />
<span className="icon" onClick={toggle}>
{isVisible ? <EyeOutlined /> : <EyeInvisibleOutlined />}
</span>
</div>
);
}
usage:
<div>
<ShowHidePassword name="name" />
<ShowHidePassword name="password" />
</div>
when you use one state for more than one thing, state changes will effect on all of elements that use the state.
best way is create a reusable input component and import it anywhere and As much as you want. on this way every changes will happen just inside that component and wont effect on others component
dont forget to pass ...props to your input if you want access to the input onChange,name and ....
export const PasswordInput = (props) => {
const [hide, setHide] = useState(false);
const toggle = () => {
setHide((prev) => !prev);
};
return (
<div>
<input type={!isVisible ? "password" : "text"} {...props} required />
<i className="icon" onClick={toggle}>
{hide ? <EyeVisible /> : <EyeInvisible />}
</i>
</div>
);
}
usage:
<div>
<PasswordInput />
<PasswordInput />
<PasswordInput />
</div>
now every toggle will only effect on the clicked input and there is no side effect and re render

react-router-dom how to redirect after button click in component

I have login component and after user singin i will got data from express server with fetch that will say all okay i want to redirect user to main page.I didn't find anything that i may use with react-router-dom to redirect user. First thing i want to now,is redirect user after he click button, without data check
this is how my Routes
ReactDOM.render(
<div>
<div><div className="nav">
<BrowserRouter>
<div className="shapka">
<Link to={linkToLogin} className="login">Войти в профиль</Link>
</div>
<div className="navItems">
<Link to='/Rings' className="navItem">Кольца</Link>
<Link to='/Earrings' className="navItem">Серёжки</Link>
<Link to='/' className="navItem">Главная страница</Link>
<Link to='/Bracelets' className="navItem">Браслеты</Link>
<Link to='/Necklace' className="navItem">Ожерелья</Link>
</div>
<Routes>
<Route path="/" element={<App/>}/>
<Route path="/Rings" element={<Rings/>}/>
<Route path="/EarRings" element={<EarRings/>}/>
<Route path="/bracelets" element={<Bracelets/>}/>
<Route path="/Necklace" element={<Necklase/>}/>
<Route path='/Profile' element={<Login/>}/>
<Route path="Register" element={<Register/>}/>
</Routes>
</BrowserRouter>
</div>
</div>
</div>,
document.getElementById('mainDiv')
);
and this is my component that located in other js file
login.addEventListener('click',e=>{
let login = document.getElementById('username')
let pwd = document.getElementById('password')
if(login.value !== '' && pwd.value !== ''){
sendToLogin({login:login.value,pwd:pwd.value})
}
})
}
render(){
return <div className="main" id="main">
<div className="registration-cssave">
<div className="form" >
<h3 className="text-center">Вход в систему</h3>
<div className="form-group">
<input className="form-control item" type="text" name="login" maxLength="15" id="inputLogin" minLength="4"
pattern="^[a-zA-Z0-9_.-]*$" id="username" placeholder="Логин" required />
</div>
<div className="form-group">
<input className="form-control item" type="password" name="password" minLength="6" id="password"
placeholder="Пароль" required/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block create-account" type="submit" id="btnEnter">Войти</button>
</div>
<div className="toRegister">
<a href="/Register" >Регистрация</a>
</div>
</div>
</div>
</div>
}
}
export default Login
You can use the useHistory hook from react-router-dom
import { useHistory } from 'react-router-dom';
function App () {
const history = useHistory();
async function logUser (userData) {
const res = await api.post("yourApi/login",{data});
if(res.status===200){
history.push("/");
}
}
render(
// Rendered parts
)
}
Additionally you can render a hidden Link component with display: "none", attach it to a ref and programmatically click it when the server response is okay. Like this:
const redirectToMain = useRef(null);
async function logUser (userData) {
const res = await api.post("yourApi/login",{data});
if(res.status===200){
redirectToMain.current.click()
}
}
render (
// *** other parts
<Link
to="/"
ref={redirectToMain}
style={{display: "none"}}
/>
)
for class components, at the first you should make a functional component then use HOC technical to use useNavigate react hook. like this:
withrouter.js:
import {useNavigate} from 'react-router-dom';
export const withRouter = WrappedComponent => props => {
return (<WrappedComponent {...props} navigate={useNavigate()}/>);
};
then use use it in other class components like this:
export default withRouter(Signin);
and use props for redirect like this:
this.props.navigate('/');

How to pass state properties from component to component

I am currently learning React and I am trying to create a basic todo list app. I am facing an issue in the understanding of how passing data from component to component.
I need that when I add a task in the modal of my home component it gets added in the "pub" state of my public task component in order for the task to be rendered.
I joined the code of both components,
Hope someone can help me :)
function PublicTask (){
const [pub,setPub] = useState([{id: 1, value : "test"},{id: 2, value : "test2"}]);
function ToDoPublicItem() {
const pubT = pub.map(value =>{
return(
<div className= 'pubTask-item'>
<li>{value.value}</li>
</div>
)
});
return(
<div>
<div>
{pubT}
</div>
</div>
);
}
return(
<div className= 'item-container'>
<h2 style={{color:'white'}}>Public Tasks</h2>
<ToDoPublicItem/>
</div>
);
}
export default PublicTask;
function Home() {
const [show,setShow] = useState(false);
const [pubTask,setPubTask] = useState([]);
function openModal() {
setShow(true);
}
function Modal(){
const[textTodo, setTextTodo] = useState('')
const addItem = () => {
const itemTopush = textTodo;
pubTask.push(itemTopush);
}
return(
<div className='modal'>
<div className = 'modal-title'>
<h2>ADD A TODO</h2>
<hr></hr>
</div>
<div className= 'modal-body'>
<input type='text' onChange = {(e) => setTextTodo(e.target.value)}/>
<input type="checkbox" id="pub" value ='public'/>
<label Htmlfor="pub">Public</label>
<input type="checkbox" id="priv" value= 'private '/>
<label Htmlfor="riv">Private</label>
<hr></hr>
<Button id='button-add' size='large' style={{backgroundColor : 'white'}} onClick={()=> addItem()}>ADD</Button>
<hr></hr>
<Button id='button-close' size='large' style={{backgroundColor : '#af4c4c'}} onClick= {()=> setShow(false)} >CLOSE</Button>
</div>
</div>
)
}
return(
<div>
<h1 style={{textAlign:'center'}}>You are logged in !</h1>
<div>
<button id='button-logout' onClick = {() => firebaseApp.auth().signOut()}>Logout</button>
</div>
<div>
<Fab color="primary" aria-label="add" size = 'large' onClick = {() => openModal()}>
<Add/>
</Fab>
{show ? <Modal/> : <div></div>}
</div>
<div>
<Router>
<div className='pub-container'>
<Link to='/publicTasks'>Public Tasks </Link>
</div>
<div className='ongo-container'>
<Link to='/onGoingTasks'>On Going Tasks </Link>
</div>
<div className='finish-container'>
<Link to='/finishedTasks'>Finished Tasks </Link>
</div>
<Route path='/publicTasks' component = {PublicTask}/>
<Route path='/onGoingTasks' component = {OngoingTask}/>
<Route path='/finishedTasks' component = {FinishedTask}/>
</Router>
</div>
</div>
);
}
export default Home;
You can share data between react components like this:
const [value, setValue] = useState("test"); // data that you want to share
return (
<Parent data={value}> // pass data to parent in your child component
);
<h1>{this.props.data}</h1> // do something with the data in the parent component

Update Avatar value from Text feild

I am very new with React.JS as well as new for JS. I have to update avatar value from a text field. This is just a small demo project. My target is as below:
It means that if some one has entered Text in
Nick name
Then Avatar Text must be updated. My render method in App.js as below
return (
<div className="App">
<div style={avatarParentContainer}>
<div style={divStyleAvatar}>
</div>
<span style={avatarContainer}>
{avatar}
</span>
</div>
<div>
<Login/>
</div>
</div>
);
Below is my Avatar
avatar= <Avatar
name={this.setState({avatarname:''})}
size="200"
round={true}
style={avatarClass}
/>;
As in above code i have one separate component as
Login
This Login component have Nick Name field as below:
<TextField
hintText="Enter your Nick name"
floatingLabelText="Nick Name"
onChange = {(event,newValue)=>this.setState({nickname:newValue})}
/>
I know this is silly question for expert person but in my case i am struggling with it. Please help me to get rid this issue.
Move state to your App component. Avatar and Login should be a stateless components. Then you can pass function as a prop to Login, and name from state to Avatar component. Something like this (not tested, because I don't have code of these comopnents ;) ):
const Login = ({ onChange }) => (
<TextField
hintText="Enter your Nick name"
floatingLabelText="Nick Name"
onChange = {(event, newValue) => onChange(newValue)}
/>
);
const Avatar = ({ name }) => (
<Avatar
name={name}
size="200"
round={true}
style={avatarClass}
/>
);
And in App:
return (
<div className="App">
<div style={avatarParentContainer}>
<div style={divStyleAvatar}>
<span style={avatarContainer}>
<Avatar name={this.state.avatarname} />
</span>
</div>
<div>
<Login onChange={avatarname => this.setState({avatarname})} />
</div>
);

Resources