React onChange function to LogIn not working - javascript

I have been on this for 10 hours and It is frustrating to say I have no clue what I am missing.
I am making a simple react app with a Sign-up and log-in function,
I have successfully created mypage, and sign up and here are two things I want to achieve and cannot get it done:
1. it should be called handleResponseSuccess callback after log in
2. it should log in successfully when button click
even a simple point out will be a huge help.. thank you
App.js
import React from "react";
import { Switch, Route, Redirect, withRouter } from "react-router-dom";
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Mypage from "./pages/Mypage";
import axios from "axios";
class App extends React.Component {
constructor(props) {
super(props);
this.staate = {
isLogin: false,
userinfo: null,
}
this.handleResponseSuccess = this.handleResponseSuccess.bind(this)
this.handler = this.handler.bind(this)
}
handleResponseSuccess() {
axios.get('http://localhost:4000/user')
.then((res) => {
this.setState({ isLogin: true, userinfo: { ...res.data }, });
this.props.history.push("/");
});
}
handler() {
axios.post('https://localhost:4000/signout', null, {
withCredentials: true,
})
.then((res) => {
this.setState({
isLogin: false,
});
this.props.history.push('/')
})
.catch((err) => alter(err));
}
render() {
const { isLogin, userinfo } = this.state;
return (
<div>
<Switch>
<Route
path='/login'
render={() => (
<Login handleResponseSuccess={this.handleResponseSuccess} />
)}
/>
<Route exact path='/signup' render={() => <Signup />} />
<Route
exact
path='/mypage'
render={() => <Mypage userinfo={userinfo} handleLogout={this.handler}/>}
/>
<Route
path='/'
render={() => {
if (isLogin) {
return <Redirect to='/mypage' />;
}
return <Redirect to='/login' />;
}}
/>
</Switch>
</div>
);
}
}
export default withRouter(App);
login.js
import React from "react";
import { Link, withRouter } from "react-router-dom";
import axios from "axios";
axios.defaults.withCredentials = true;
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
errorMessage: ""
};
this.handleInputValue = this.handleInputValue.bind(this);
this.handleLogin = this.handleLogin.bind(this)
}
handleInputValue = (key) => (e) => {
this.setState({ [key]: e.target.value });
};
handleLogin = () => {
if (this.state.email.length === 0 || this.state.password.length === 0) {
return this.setState({ errorMessage : "enter email and password"})
}
axios.defaults.withCredentials = true;
axios.post("https://localhost:4000/signin", {
email: this.state.email,
password : this.state.password
}).then(() => {
this.props.handleResponseSuccess()
})
};
render() {
return (
<div>
<center>
<h1>Sign In</h1>
<form onSubmit={(e) => e.preventDefault()}>
<div>
<span>email</span>
<input type='email' onChange={this.handleInputValue("email")}></input>
</div>
<div>
<span>password</span>
<input type='password' onChange={this.handleInputValue("password")}></input>
</div>
<div>
<Link to='/signup'>no id?</Link>
</div>
<button className='btn btn-login' type='submit' onClick={this.handleLogin}>
log in
</button>
{this.state.errorMessage ? <div className="alert-box">{this.state.errorMessage}</div> : null}
</form>
</center>
</div>
);
}
}
export default withRouter(Login);

in your code in App.js
this.staate = {
isLogin: false,
userinfo: null,
}
and you are calling it as
const { isLogin, userinfo } = this.state;
maybe this could be of some help

Related

Pagination is not working in React Frontend

Pagination is not working in React Frontend. I am creating an Ecommerce Application and want to Paginate my products on my website. Here is the Code for Getting Product with Pagination.
const getProducts = asyncHandler(async (req, res, next) => {
const pageSize = 3;
const page = Number(req.query.pageNumber) || 1;
const keyword = req.query.keyword
? {
name: {
$regex: req.query.keyword,
$options: 'i',
},
}
: {};
const count = await Product.countDocuments({ ...keyword });
const products = await Product.find({ ...keyword })
.limit(pageSize)
.skip(pageSize * (page - 1))
.sort({ _id: -1 });
res.json({ products, page, pages: Math.ceil(count / pageSize) });
});
I am using Redux for state Management. Here is the code for the Reducer
// PRODUCT LIST
export const productListReducer = (state = { products: [] }, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return { loading: true, products: [] };
case PRODUCT_LIST_SUCCESS:
return {
loading: false,
pages: action.payload.pages,
page: action.payload.page,
products: action.payload.products,
};
case PRODUCT_LIST_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
And here is the Action code
// PRODUCT LIST
export const listProduct =
(keyword = ' ', pageNumber = ' ') =>
async (dispatch) => {
try {
dispatch({ type: PRODUCT_LIST_REQUEST });
const { data } = await axios.get(
`/api/products?keyword=${keyword}&pageNumber=${pageNumber}`
);
dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: PRODUCT_LIST_FAIL,
payload:
error.response && error.response.data.error
? error.response.data.error
: error.message,
});
}
};
And this is the Component for pagination and few others
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import Rating from './Rating';
import Pagination from './pagination';
import { useDispatch, useSelector } from 'react-redux';
import { listProduct } from '../../Redux/Actions/ProductActions';
import Loading from '../LoadingError/Loading';
import Message from '../LoadingError/Error';
const ShopSection = (props) => {
const { keyword, pagenumber } = props;
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { loading, error, products, page, pages } = productList;
useEffect(() => {
dispatch(listProduct(keyword, pagenumber));
}, [dispatch, keyword, pagenumber]);
return (
<>
<div className='container'>
<div className='section'>
<div className='row'>
<div className='col-lg-12 col-md-12 article'>
<div className='shopcontainer row'>
{loading ? (
<div className='mb-5'>
<Loading />
</div>
) : error ? (
<Message variant='alert-danger'>{error}</Message>
) : (
<>
{products.map((product) => (
<div
className='shop col-lg-4 col-md-6 col-sm-6'
key={product._id}
>
<div className='border-product'>
<Link to={`/products/${product._id}`}>
<div className='shopBack'>
<img src={product.image} alt={product.name} />
</div>
</Link>
<div className='shoptext'>
<p>
<Link to={`/products/${product._id}`}>
{product.name}
</Link>
</p>
<Rating
value={product.rating}
text={`${product.numReviews} reviews`}
/>
<h3>${product.price}</h3>
</div>
</div>
</div>
))}
</>
)}
{/* Pagination */}
<Pagination
pages={pages}
page={page}
keyword={keyword ? keyword : ''}
/>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default ShopSection;
And Pagination Component is here
import React from 'react';
import { Link } from 'react-router-dom';
const Pagination = (props) => {
const { page, pages, keyword = '' } = props;
return (
pages > 1 && (
<nav>
<ul className='pagination justify-content-center'>
{[...Array(pages).keys()].map((x) => (
<li
className={`page-item ${x + 1 === page ? 'active' : ''}`}
key={x + 1}
>
<Link
className='page-link'
to={
keyword
? `/search/${keyword}/page/${x + 1}`
: `/page/${x + 1}`
}
>
{x + 1}
</Link>
</li>
))}
</ul>
</nav>
)
);
};
export default Pagination;
And here is my App.js component
import React from 'react';
import './App.css';
import './responsive.css';
import 'react-toastify/dist/ReactToastify.css';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import HomeScreen from './screens/HomeScreen';
import SingleProduct from './screens/SingleProduct';
import Login from './screens/Login';
import Register from './screens/Register';
import CartScreen from './screens/CartScreen';
import ShippingScreen from './screens/ShippingScreen';
import ProfileScreen from './screens/ProfileScreen';
import PaymentScreen from './screens/PaymentScreen';
import PlaceOrderScreen from './screens/PlaceOrderScreen';
import OrderScreen from './screens/OrderScreen';
import NotFound from './screens/NotFound';
import PrivateRouter from './PrivateRoute';
const App = () => {
return (
<Router>
<Switch>
<Route path='/' component={HomeScreen} exact />
<Route path='/search/:keyword' component={HomeScreen} exact />
<Route path='/page/:pagenumber' component={HomeScreen} exact />
<Route
path='/search/:keyword/page/:pageNumber'
component={HomeScreen}
exact
/>
<Route path='/products/:id' component={SingleProduct} />
<Route path='/login' component={Login} />
<Route path='/register' component={Register} />
<PrivateRouter path='/profile' component={ProfileScreen} />
<Route path='/cart/:id?' component={CartScreen} />
<PrivateRouter path='/shipping' component={ShippingScreen} />
<PrivateRouter path='/payment' component={PaymentScreen} />
<PrivateRouter path='/placeorder' component={PlaceOrderScreen} />
<PrivateRouter path='/order/:id' component={OrderScreen} />
<Route path='*' component={NotFound} />
</Switch>
</Router>
);
};
export default App;
When i try to send request from postman the data is returning fine. I have already debug alot and couldn't able to resolve this bug. Help me to resolve this i will be grateful to you
You have to define pagenumber, because it is not defined.
const pagenumber = match.params.pagenumber;
Define it like this.
I hope it will work.

How do I implement a Logout functionality from a React Router Link to imports an array?

I am trying to implement a logout functionality and am not sure what I need to do to implement it with my current set up. I was able to set up a Login functionality that pulls the correct profile data and once logged in, the Navbar + Dropdown Items are updated to display a link to logout. However, I am unsure of how I can add the logout functionality at the moment. I have been following guides but am stuck since nothing I am trying is working, once I hit a the logout link it links to /logout but fails to actually logout. The tutorial I am following claims I can do it via the frontend (I am using JWT).
Navbar.jsx:
import React, {useState, useEffect} from 'react';
import {Link} from 'react-router-dom';
import './Navbar.css';
import Dropdown from './Dropdown';
import {NavItemsList} from './NavItemsList';
import NavItems from './NavItems';
function Navbar() {
const [click, setClick] = useState(false);
const [button, setButton] = useState(true);
const [dropdown, setDropdown] = useState(false);
const handleClick = () => setClick(!click);
const closeMobileMenu = () => setClick(false);
const onMouseEnter = () => {
if (window.innerWidth < 960) {
setDropdown(false);
} else {
setDropdown(true);
}
};
const onMouseLeave = () => {
if (window.innerWidth < 960) {
setDropdown(false);
} else {
setDropdown(false);
}
};
return (
<>
<nav className='navbar'>
<div className='navbar-container-whole'>
<div className='left-nav-container'>
{/* Link in react-router-dom essentially replaces a tag.*/}
<Link to='/' className='navbar-logo'>
<img src='/images/logo.png' className='hashtek-logo' alt='logo' />
<h1 className='navbar-name'>HashTek</h1>
</Link>
</div>
{/* .navbar-container will create a div with that class name. */}
<div className='center-nav-container'>
<form action='./' method='get' id='search-form'>
<div class='searchbar'>
<input
class='searchbar_input'
type='search'
name='search'
placeholder='Search..'
/>
<button type='submit' class='searchbar_button'>
<i class='material-icons'>search</i>
</button>
</div>
</form>
</div>
<div className='right-nav-container'>
<ul className={click ? 'nav-menu active' : 'nav-menu'}>
<div className='text-links'>
{/* This line above is for when you are on mobile, and an item is clicked, the nav menu will disappear */}
{NavItemsList.slice(0, 4).map((menu, index) => {
return <NavItems items={menu} key={index} />;
})}
</div>
<div className='logo-links'>
{NavItemsList.slice(4, 6).map((menu, index) => {
return <NavItems items={menu} key={index} />;
})}
</div>
</ul>
<div className='menu-icon' onClick={handleClick}>
<i className={click ? 'fas fa-times' : 'fas fa-bars'} />
</div>
</div>
</div>
</nav>
</>
);
}
export default Navbar;
NavItemList.js:
export const NavItemsList = [{
title: 'Products',
path: '/products',
cName: 'nav-links',
},
{
title: 'Stats',
path: '/stats',
cName: 'nav-links',
},
{
title: 'Contacts',
path: '/contacts',
cName: 'nav-links',
subNav: [{
title: 'About',
path: '/contacts/about',
cName: 'dropdown-link',
menuName: 'contacts-menu',
},
{
title: 'How To',
path: '/contacts/how-to',
cName: 'dropdown-link',
menuName: 'contacts-menu',
},
{
title: 'Developers',
path: '/contacts/developers',
cName: 'dropdown-link',
menuName: 'contacts-menu',
},
{
title: 'Designers',
path: '/contacts/designers',
cName: 'dropdown-link',
menuName: 'contacts-menu',
},
{
title: 'Mentors',
path: '/contacts/mentors',
cName: 'dropdown-link',
menuName: 'contacts-menu',
},
],
},
{
title: 'Services',
path: '/services',
cName: 'nav-links',
subNav: [{
title: 'Streaming',
path: '/services/streaming',
cName: 'dropdown-link',
menuName: 'services-menu',
},
{
title: 'Editing',
path: '/services/editing',
cName: 'dropdown-link',
menuName: 'services-menu',
},
],
},
{
title: Account,
path: '/my-account',
cName: 'nav-links',
subNav: [{
title: 'Login',
path: '/login',
cName: 'dropdown-link',
menuName: 'account-menu',
authenticated: false,
},
{
title: 'Logout',
path: '/logout',
cName: 'dropdown-link',
menuName: 'account-menu',
authenticated: true,
},
{
title: 'Profile',
path: '/profile',
cName: 'dropdown-link',
menuName: 'account-menu',
},
],
},
{
title: Help,
path: '/help',
cName: 'nav-links',
},
];
Login.jsx:
import React, {useState, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {Link} from 'react-router-dom';
import {Formik, Field, Form, ErrorMessage} from 'formik';
import * as Yup from 'yup';
import {login} from '../../slices/auth';
import {clearMessage} from '../../slices/messages';
const Login = (props) => {
const [loading, setLoading] = useState(false);
const {isLoggedIn} = useSelector((state) => state.auth);
const {message} = useSelector((state) => state.message);
const dispatch = useDispatch();
useEffect(() => {
dispatch(clearMessage());
}, [dispatch]);
const initialValues = {
username: '',
password: '',
};
const validationSchema = Yup.object().shape({
username: Yup.string().required('This field is required!'),
password: Yup.string().required('This field is required!'),
});
const handleLogin = (formValue) => {
const {username, password} = formValue;
setLoading(true);
dispatch(login({username, password}))
.unwrap()
.then(() => {
props.history.push('/profile');
window.location.reload();
})
.catch(() => {
setLoading(false);
});
};
if (isLoggedIn) {
return <Link to='/profile' />;
}
return (
<div className='login-form'>
<div className='card card-container'>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleLogin}
>
<Form>
<div className='form-group'>
<label htmlFor='username'>Username</label>
<Field name='username' type='text' className='form-control' />
<ErrorMessage
name='username'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<label htmlFor='password'>Password</label>
<Field name='password' type='password' className='form-control' />
<ErrorMessage
name='password'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary btn-block'
disabled={loading}
>
{loading && (
<span className='spinner-border spinner-border-sm'></span>
)}
<span>Login</span>
</button>
</div>
</Form>
</Formik>
</div>
{message && (
<div className='form-group'>
<div className='alert alert-danger' role='alert'>
{message}
</div>
</div>
)}
</div>
);
};
export default Login;
auth.service.js:
//Authentication Service file. This service uses Axios for HTTP requests and Local Storage for user information & JWT.
// It provides following important functions:
// register(): POST {username, email, password}
// login(): POST {username, password} & save JWT to Local Storage
// logout(): remove JWT from Local Storage
import axios from 'axios';
const API_URL = 'http://localhost:8080/api/auth/';
const register = (username, email, password) => {
return axios.post(API_URL + 'signup', {
username,
email,
password,
});
};
const login = (username, password) => {
return axios
.post(API_URL + 'login', {
username,
password,
})
.then((response) => {
if (response.data.accessToken) {
localStorage.setItem('user', JSON.stringify(response.data));
}
return response.data;
});
};
const logout = () => {
localStorage.removeItem('user');
};
const authService = {
register,
login,
logout,
};
export default authService;
auth.js:
// We’re gonna import AuthService to make asynchronous HTTP requests with trigger one or more dispatch in the result.
// – register(): calls the AuthService.register(username, email, password) & dispatch setMessage if successful/failed
// – login(): calls the AuthService.login(username, password) & dispatch setMessage if successful/failed
// – logout(): calls the AuthService.logout().
// setMessage is imported from message slice that we’ve created above.
// We also need to use Redux Toolkit createAsyncThunk which provides a thunk that will take care of the action types and dispatching the right actions based on the returned promise.
//There are 3 async Thunks to be exported:
// register
// login
// logout
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
'auth/register',
async ({username, email, password}, thunkAPI) => {
try {
const response = await AuthService.register(username, email, password);
thunkAPI.dispatch(setMessage(response.data.message));
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const login = createAsyncThunk(
'auth/login',
async ({username, password}, thunkAPI) => {
try {
const data = await AuthService.login(username, password);
return {user: data};
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const logout = createAsyncThunk('auth/logout', async () => {
await AuthService.logout();
});
const initialState = user
? {isLoggedIn: true, user}
: {isLoggedIn: false, user: null};
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[register.fulfilled]: (state, action) => {
state.isLoggedIn = false;
},
[register.rejected]: (state, action) => {
state.isLoggedIn = false;
},
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
},
});
const {reducer} = authSlice;
export default reducer;
App.js:
import React, {useState, useEffect, useCallback} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'; //Switch was replaced by Routes in react-router-dom v6
import './App.css';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
import Home from './components/pages/Home';
import Account from './components/pages/Account';
import Login from './components/pages/Login';
import Profile from './components/pages/Profile';
import {logout} from './slices/auth';
import EventBus from './common/EventBus';
const App = () => {
const {user: currentUser} = useSelector((state) => state.auth);
const dispatch = useDispatch();
const logOut = useCallback(() => {
dispatch(logout());
}, [dispatch]);
useEffect(() => {
EventBus.on('logout', () => {
logOut();
});
return () => {
EventBus.remove('logout');
};
}, [currentUser, logOut]);
return (
<>
<Router>
<Navbar />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/my-account' element={<Account />} />
<Route path='/login' element={<Login />} />
{currentUser ? (
<Route path='/logout' onClick={logOut} />
) : ( //Did something wrong here I believe. Not sure if I need to include this in another file.
<Route path='/login' element={<Login />} />
)}
<Route path='/profile' element={<Profile />} />
</Routes>
<Footer />
</Router>
</>
);
};
export default App;
I feel like I am close, just missing something or have something in the wrong place. If anyone can help me, I would deeply appreciate it. Sorry if I included too much code, just want everyone to have a good idea of whats going on. Thank you!
It seems you only need a Logout component that dispatches the logout action when it mounts, waits for the action to complete, and likely redirect back to a homepage or similar.
Example:
const Logout = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
dispatch(logout())
.then(() => {
navigate("/", { replace: true });
});
}, []);
return <LoadingSpinner />;
};
App
There likely also isn't a string need to conditionally render the routes, just render them normally.
...
import Login from './components/pages/Login';
import Logout from './components/pages/Logout';
...
...
<Router>
<Navbar />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/my-account' element={<Account />} />
<Route path='/login' element={<Login />} />
<Route path='/logout' element={<LogOut />} />
<Route path='/profile' element={<Profile />} />
</Routes>
<Footer />
</Router>

this.props.onSave is not a function- Uncaught typescript error

I have a form and when someone saves it I want it to display it in a blog format on my first page. I am getting an error when I click on the "Save" button. The error is:
Unhandled Rejection (TypeError): _this.props.onSave is not a function
On the line below:
handleSave = (e) => e.preventDefault();
const id = this.prop .onSave(this.state.post);
How can I resolve this error? I am using Nextjs with Typescript.
Here are both my files App.tsx(Routing) and NewQuestion.tsx(Save button)
export default class NewQuestion extends React.Component<Props> {
state = {
post: {title: '', fullName: '',body: '',createdAt: '',updatedAt: '' }}
updateValue = (e: { target: { name: any; value: any; }; }) => {
const { post } = this.state;
this.setState({
post: {...post,[e.target.name]: e.target.value} });}
handleSave = async (e: { preventDefault: () => void; }) => {
e.preventDefault();
const id = await this
.props
.onSave(this.state.post);
this.props .history .replace(`/posts/${id}`)}
render() {
const { post } = this.state;
return ( <Box> <div className="post-form"><Title>
<TextName>New Question</TextName>
</Title>
<div className="post-form-field">
{/* <label>Full Name</label> */}
<H5 >Full Name
<Inputbox type="text" name="fullName" value={post.fullName}
onChange={this.updateValue} />
</H5>
</div>
</div>
<form onSubmit={this.handleSave}>
<div className="post-form-field post-form-field-text">
<QuestionText >Question</QuestionText>
<Questionbox
data-testid='quest'
name="body"
value={post.body}
placeholder="Start your question”
onChange={this.updateValue} /></div>
<div className="post-form-buttons">
<Button type="submit" data-testid='add-question' value="Save">
Add Question </Button>
<Cancel>
<Link href={`/`}><A>Cancel</A></Link>
</Cancel>
</div></form></Box>);}}
App.tsx
// import statements
class App extends React.Component<{},
Props> {
state = {
db: new DB('QA'),
posts: {},
loading: true};
async componentDidMount() {
const posts = await this
.state
.db
.getAllPosts();
this.setState({ posts, loading: false });}
handleSave = async (post) => {
let { id } = await this
.state
.db
.createPost(post);
const { posts } = this.state;
this.setState({
posts: {
...posts,
[id]: post}});
return id;}
render() {
if (this.state.loading) {
return <h2>Loading, please wait...</h2>}
const LocationDisplay = withRouter(({ location }) => (
<div data-testid="location-display">{location.pathname}</div>
))
return (
<div className="All-Routes">
<Switch>
<Route
exact
path="/"
component={props => (<IndexPage {...props} posts={this.state.posts} />)} />
<Route
path="/posts/:id"
component={(props) => <ShowPage {...props} post={this.state.posts[props.match.params.id]} />} />
<Route
exact
path="/new"
component={(props) => <NewQuestion {...props} onSave={this.handleSave} />} />
</Switch>
<LocationDisplay />
</div>
);
}
}
export default App

React - render CSS animation onClick

I'm new to React, sorry if this is too basic.
I am trying to render a simple animation when <Link> is clicked in React.
I have Coffees.jsx:
import Brewing from './Brewing.jsx';
handleClick() {
return (<Brewing/>)
}
render(){
return (
<div>
<div>
<Link onClick={this.handleClick} to="/brewing">Coffee</Link>
</div>
</div>
);
}
}
export default Menus;
and Brewing.jsx:
import './css/mug.css'
class Brewing extends Component {
constructor (props) {
super(props);
};
render() {
return (
<div>
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
</div>
);
}
}
export default Brewing;
The above is not working. It only works if I inject the animation:
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
directly into Coffees.jxs, like so:
render(){
return (
<div>
<div>
<div className="cup">
<div className="coffee"></div>
</div>
<div className="smoke"></div>
<Link to="/brewing"></Link>
</div>
</div>
);
}
But this is not desired...How do I render this animation at onClick?
EDIT:
App.jsx
class App extends Component {
constructor() {
super();
this.state = {
users: [],
isAuthenticated: false,
messageName: null,
messageType: null,
select:'',
email: '',
id: '',
username: '',
active: '',
admin: '',
//task:''
};
this.logoutUser = this.logoutUser.bind(this);
this.loginUser = this.loginUser.bind(this);
this.createMessage = this.createMessage.bind(this);
this.removeMessage = this.removeMessage.bind(this);
this.userId = this.userId.bind(this);
};
componentWillMount() {
if (window.localStorage.getItem('authToken')) {
this.setState({ isAuthenticated: true });
};
};
componentDidMount() {
this.getUsers();
this.userId();
};
getUsers() {
axios.get(`${process.env.REACT_APP_WEB_SERVICE_URL}/users`)
.then((res) => { this.setState({ users: res.data.data.users }); })
.catch((err) => { });
};
logoutUser() {
window.localStorage.clear();
this.setState({ isAuthenticated: false });
};
loginUser(token) {
window.localStorage.setItem('authToken', token);
this.setState({ isAuthenticated: true });
this.getUsers();
this.createMessage('Welcome', 'success');
};
userId(event) {
const options = {
url: `${process.env.REACT_APP_WEB_SERVICE_URL}/auth/status`,
method: 'get',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${window.localStorage.authToken}`
}
};
return axios(options)
.then((res) => {
console.log(res.data.data)
this.setState({
select: res.data.data.select,
email: res.data.data.email,
id: res.data.data.id,
username: res.data.data.username,
active: String(res.data.data.active),
admin: String(res.data.data.admin),
})
})
.catch((error) => { console.log(error); });
};
createMessage(name='Sanity Check', type='success') {
this.setState({
messageName: name,
messageType: type
});
setTimeout(() => {
this.removeMessage();
}, 3000);
};
removeMessage() {
this.setState({
messageName: null,
messageType: null
});
};
render() {
return (
<div>
<NavBar
title={this.state.title}
isAuthenticated={this.state.isAuthenticated}
/>
<section className="section">
<div className="container">
{this.state.messageName && this.state.messageType &&
<Message
messageName={this.state.messageName}
messageType={this.state.messageType}
removeMessage={this.removeMessage}
/>
}
<div className="columns">
<div className="column is-half">
<br/>
<Switch>
<Route exact path='/about' component={About}/>
<Route exact path='/register' render={() => (
<Form
formType={'Register'}
isAuthenticated={this.state.isAuthenticated}
loginUser={this.loginUser}
createMessage={this.createMessage}
userId={this.state.id}
/>
)} />
<Route exact path='/login' render={() => (
<Form
formType={'Login'}
isAuthenticated={this.state.isAuthenticated}
loginUser={this.loginUser}
createMessage={this.createMessage}
userId={this.state.id}
/>
)} />
<Route exact path='/logout' render={() => (
<Logout
logoutUser={this.logoutUser}
isAuthenticated={this.state.isAuthenticated}
/>
)} />
<Route exact path='/status' render={() => (
<UserStatus
isAuthenticated={this.state.isAuthenticated}
/>
)} />
<Route exact path='/seeds' render={() => (
<Seeds
isAuthenticated={this.state.isAuthenticated}
userId={this.state.id}
/>
)} />
<Route exact path='/menus' render={() => (
<Menus
isAuthenticated={this.state.isAuthenticated}
userId={this.state.id}
/>
)} />
<Route exact path='/coffee' render={() => (
<Coffees
isAuthenticated={this.state.isAuthenticated}
userId={this.state.select}
/>
)} />
</Switch>
</div>
</div>
</div>
</section>
</div>
)
}
};
export default App;
When you click any Link it will not wait on that component to execute any event, it will simply redirect to given path (to="/coffees"), so you need Route to handle this path.
Instead of Link we can use a button or simply a div (you can style it so that it look like link) and write onClick handler on that. In that handler we need to add a setTimeout with the timeout of actual animation.
Now when setTimeout executes, we can set a variable in state which will help us to redirect to desired component.
Your menu component should be,
class Menu extends React.Component{
constructor(props){
super(props);
this.state = {
isLoading: false,
redirect: false
}
}
gotoCoffee = () => {
this.setState({isLoading:true})
setTimeout(()=>{
this.setState({isLoading:false,redirect:true})
},5000) //Replace this time with your animation time
}
renderCoffee = () => {
if (this.state.redirect) {
return <Redirect to='/coffees' />
}
}
render(){
return(
<div>
{this.state.isLoading && <Brewing />}
{this.renderCoffee()}
<div onClick={this.gotoCoffee} style={{textDecoration:'underline',color:'blue',cursor:'pointer'}}>Go to Coffee</div>
</div>
)
}
}
I have used Redirect from react-router-dom package for navigation.
Demo
There are a few things to consider with your Menus component;
returning the <Brewing> JSX from handleClick() won't affect the rendering result of the Menus component, meaning that the animation in <Brewing> won't show as required
you'll need to track some state to determine if the <Link> has been clicked by the user at which point, your Menus component can render the <Brewing> component (ie that contains the animation)
One way to approach that in code would be to make the following changes in Coffee.jsx:
import Brewing from './Brewing.jsx';
class Menus extends React.Component {
constructor(props) {
super(props);
/*
Set inital state to false, which means Brewing wont initially show
*/
this.state = { hasBeenClicked : false };
}
}
handleClick() {
/*
Set hasBeenClicked state of Menus to true after click which will
cause Brewing to be rendered
*/
this.setState({ hasBeenClicked : true });
}
render(){
return (
<div>
<div>
{ /*
This is short hand for "if hasBeenClicked = true, then
render Brewing here
*/ }
{ (this.state.hasBeenClicked === true) && <Brewing/> }
<Link onClick={this.handleClick} to="/brewing">Coffee</Link>
</div>
</div>
);
}
}
export default Menus;

React Router 4 Async Rendering

I am following the guide on React Router 4 for Redirect(Auth) and I am having trouble rendering base on the promise the ajax returns. I'm not sure why my rendering inside the promise is not being returned. Could someone point me to the right direction?
import React from 'react';
import {
Route,
Redirect,
withRouter
} from 'react-router-dom';
import HeaderContainer from '../containers/HeaderContainer';
const PrivateRoute = ({ component: Component, ...props }) => {
const validated = (rest) => {
props.fetchUser()
.then(() => {
return (
<div>
<HeaderContainer />
<Component {...rest}/>
</div>
)
})
.catch(()=> {
return (
<Redirect to={{
pathname: '/signin',
state: { from: props.location }
}}/>
)
}
);
}
return (
<Route {...props} render={rest => {
return (
<div>
{ validated(rest) }
</div>
)
}}/>
)
}
export default withRouter(PrivateRoute);
My routes look like this
const Root = ({ store }) => {
return (
<Provider store={ store }>
<BrowserRouter onUpdate={() => window.scrollTo(0, 0)}>
<div className="root">
<Switch>
<Route exact path="/signin" component={SignInContainer}/>
<PrivateRouteContainer exact path="/" component={HomePageContainer} />
</Switch>
</div>
</BrowserRouter>
</Provider>
)
};
Thats because promise cannot return value, it only returns Promise. Instead it execute callbacks. Here is some explanation.
You could rearrange your code to somewhat like this:
class PrivateRoute extends React.Component {
constructor(props){
super(props);
this.state = {
isFetching: true,
isSuccess: null,
};
}
componentDidMount() {
this.props.fetchUser()
.then(() => {
this.setState({ isFetching: false, isSuccess: true });
})
.catch(()=> {
this.setState({ isFetching: false, isSuccess: false });
});
}
render() {
const { isFetching, isSuccess } = this.state;
return (
<Route {...this.props} render={rest => {
const success = (
<div>
<HeaderContainer />
<Component {...rest}/>
</div>
);
const error = (
<Redirect to={{
pathname: '/signin',
state: { from: this.props.location }
}}/>
);
if(isFetching) {
return null;
}
return isSuccess ? success : error;
}}/>
)
}
}
Notice that Promise doesn't return anything it just executing a callback which triggers rerender with new data in state.

Categories

Resources