8-Bit Borges
8-Bit Borges

Reputation: 10043

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;

Upvotes: 1

Views: 3834

Answers (2)

ravibagul91
ravibagul91

Reputation: 20765

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

Upvotes: 1

Dacre Denny
Dacre Denny

Reputation: 30390

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;

Upvotes: 1

Related Questions