React - render CSS animation onClick - javascript

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;

Related

React onChange function to LogIn not working

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

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

Passing props to NavBar doesn't update component

I am building an ad website. I built a registration system, which works perfectly fine, but for some reason I can't update the NavBar based on the event that has happened. For example, I want to replace the NavLink called "LOGIN/REGISTER" with "LOGGED IN". I have passed the props of the User.ID from the parent component (App.js) into the other components without any problem, but cannot do this for the NavBar. If I try a console.log - it would say undefined. I am going to put a couple of codes demonstrating where it works and where it does not:
APP.JS
*imports, which I am skipping*
const cookies = new Cookies();
class App extends Component {
constructor(){
super();
this.state = {
}
this.LogUser = this.LogUser.bind(this);
this.LogoutUser = this.LogoutUser.bind(this);
}
LogUser(User, ID){
cookies.set('User', User, { path: '/' });
cookies.set('UserID', ID,{ path: '/'});
}
LogoutUser(){
cookies.remove('User')
}
render() {
return (
<div>
<div>
//MENU <- WHERE I CAN'T PASS THE PROPS OF USER AND USERID
<Menu render={(props) => <Menu {...props} User={cookies.get('User')} ID={cookies.get('UserID')} LogOutUser={this.LogoutUser} />}/>
</div>
<Router history = {history} >
<div>
//I have removed all other routes as they are not needed, but here is an example, in which the passing of props works
<Route path = "/Profile" render={(props) => <Profile {...props} User={cookies.get('User')} ID={cookies.get('UserID')} LogOutUser={this.LogoutUser} />}/>
</div>
</Router>
</div>
);
}
}
export default App;
And for example in Profile.jsx, I can do that:
PROFILE.JSX
export default class Profile extends Component {
constructor(props, context) {
super(props, context);
this.state = {
LoggedUser: '',
UserID: '',
};
this.LogOutClick = this.LogOutClick.bind(this);
}
LogOutClick(){
this.props.LogOutUser();
history.push('/Logout');
}
componentDidMount(){
if (this.props.User !== undefined)
{
this.setState({LoggedUser: this.props.User, UserID: this.props.ID})
}
else
{
history.push('/Login');
}
}
render() {
return (
<div>
Hello, {this.props.User}!
<div>
)}}
But when I try it in the Menu component, I can't manage it to update accordingly:
NAVBAR.JSX
export default class Menu extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
isOpen: false,
Title: '',
};
}
toggle() {
this.setState({
isOpen: !this.state.isOpen
});
}
//here I tried to put something similar to the ComponentDidMount() in Profile.jsx, but it didn't work.
componentDidMount(){
if (this.props.User !== undefined)
{
this.setState({LoggedUser: this.props.User, UserID: this.props.ID})
this.setState({Title: "LOGGED IN"})
}
else
{
this.setState({Title: "LOGIN/REGISTER"})
}
}
render() {
console.log(this.state.User)
console.log(this.state.ID)
return (
<div>
<Navbar color="light" light expand="md">
<NavbarBrand href="/"><img src={require('./images/home.png')} width = "25px" height = "25px"/></NavbarBrand>
<NavbarToggler onClick={this.toggle} />
<Collapse isOpen={this.state.isOpen} navbar>
<Nav className="ml-auto1" navbar>
<NavItem>
<NavLink href="/Ads"><b>ADS</b></NavLink>
</NavItem>
<NavItem>
<NavLink href="/Profile"><b>YOUR PROFILE</b></NavLink>
</NavItem>
<NavItem>
//What I want to update
<NavLink href="/Login"><b>{this.state.Title}</b></NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>
</div>
);
}
}
React will only update in response to a new state or new props. You are manipulating a cookie which can't cause a component re-render. Here's a solution:
In your App component change the Log methods to:
constructor(){
super();
this.state ={
currentUserId: cookies.get('UserID'),
currentUser: cookies.get('User')
};
this.LogUser = this.LogUser.bind(this);
this.LogoutUser = this.LogoutUser.bind(this);
}
LogUser(User, ID){
cookies.set('User', User, { path: '/' });
cookies.set('UserID', ID,{ path: '/'});
this.setState({
currentUserId: ID,
currentUser: User
});
}
LogoutUser(){
cookies.remove('User');
this.setState({
currentUserId: null,
currentUser: null
});
}
And your render will become:
render() {
return (
<div>
<div>
<Menu render={(props) => <Menu {...props} User={this.state.currentUser} ID={this.state.currentUserId} LogOutUser={this.LogoutUser} />}/>
</div>
<Router history = {history} >
<div>
//I have removed all other routes as they are not needed, but here is an example, in which the passing of props works
<Route path = "/Profile" render={(props) => <Profile {...props} User={this.state.currentUser} ID={this.state.currentUserId} LogOutUser={this.LogoutUser} />}/>
</div>
</Router>
</div>
);
}

React router error (Failed prop type: Invalid prop `children` supplied to `Switch`, expected a ReactNode.)

Try to modify component, the main idea is, I want create login page, try modify App.js but get error
warning.js?6327:36 Warning: Failed prop type: Invalid prop children
supplied to Switch, expected a ReactNode.
My code is:
class App extends Component {
constructor(props) {
super(props)
}
routeWithSubRoutes(route) {
return (
<Route
key={_.uniqueId()}
exact={route.exact || false}
path={route.path}
render={props => (
// Pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes || null} />
)}
/>
);
}
render () {
return (
<div className={styles.App}>
<Helmet {...config.app} />
<NavBar />
<Switch>
{routes.map(route => this.routeWithSubRoutes.bind(this,route))}
</Switch>
</div>
)
}
}
export default App;
Code that I try to modify
export default () => {
// Use it when sub routes are added to any route it'll work
const login = () => {
}
const routeWithSubRoutes = route => (
<Route
key={_.uniqueId()}
exact={route.exact || false}
path={route.path}
render={props => (
// Pass the sub-routes down to keep nesting
<route.component {...props} routes={route.routes || null} />
)}
/>
);
var isLogin = false;
if(!isLogin) {
return (
<Login />
)
}
if(isLogin) {
return (
<div className={styles.App}>
<Helmet {...config.app} />
<NavBar />
<Switch>
{routes.map(route => routeWithSubRoutes(route))}
</Switch>
</div>
);
}
};
this code is working, but my not, how to fix this?
Function.bind doesn't call the function, it only binds its context. Instead, you should bind it in the constructur:
class App extends Component {
constructor(props) {
super(props)
this.routeWithSubRoutes = this.routeWithSubRoutes.bind(this)
}
/* ... */
render () {
return (
<div className={styles.App}>
<Helmet {...config.app} />
<NavBar />
<Switch>
{routes.map(route => this.routeWithSubRoutes(route))}
</Switch>
</div>
)
}
}

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