简体   繁体   中英

Toggling Font Awesome 5 icon with React

I am trying to toggle a Font Awesome icon by clicking on a to-do list item. Here is the entire component...

import React from 'react';

import './TodoItem.scss';

class TodoItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      complete: false
    }
    this.toggleComplete = this.toggleComplete.bind(this);
  }

  toggleComplete() {
    this.setState(prevState => ({
      complete: !prevState.complete
    }));
  }

  render() {
    const incompleteIcon = <span className="far fa-circle todo-item-icon"></span>;
    const completeIcon = <span className="far fa-check-circle todo-item-icon"></span>;

    return (
      <div className="todo-item" onClick={this.toggleComplete}>
        {this.state.complete ? completeIcon : incompleteIcon}
        <span className="todo-item-text">{this.props.item}</span>
      </div>
    );
  }
}

export default TodoItem;

Here's my FA 5 CDN (straight from the website)...

<script defer src="https://use.fontawesome.com/releases/v5.0.1/js/all.js"></script>

I've taken a few screenshots of the React dev tool and inspector...

Here is the React component while incomplete (the default) 未完成时反应组件

And while complete... 完成时反应组件

And the two icon elements in the inspector, I noticed the one not being used by default is commented out. 在此处输入图片说明

As you can see, I'm able to manually toggle the complete state and the component changes in the React tool but the change is not rendered. I changed the default state to make sure both icons are loaded correctly and they are. I made a Codepen to try it in a different environment and this one works, but I am using the FA 4.7.0 CDN. With the Codepen and FA 4.7.0, when I inspect the icon, it is just an HTML element not SVG.

I'd like to get this component working with FA 5 so any help would be appreciated!

The font-awesome javascript doesn't rerender on a React rerender trigger. If you are okay with not using the new font-awesome svg/javascript icons, you can use font-awesome as a webfont with css.

In your index.html, delete the fontawesome script, and add the font-awesome css stylesheet:

<link href="https://use.fontawesome.com/releases/v5.0.2/css/all.css" rel="stylesheet">

Your code should work now.


The other possibility is to use the official font-awesome react package (it's a bit more of a hassle, but it uses the new svg icons)

Add necessary packages to project:

yarn add @fortawesome/fontawesome @fortawesome/react-fontawesome
yarn add @fortawesome/fontawesome-free-regular @fortawesome/fontawesome-free-solid

Example code:

import fontawesome from '@fortawesome/fontawesome'
import FontAwesomeIcon from '@fortawesome/react-fontawesome'
import { faCircle as fasCircle } from '@fortawesome/fontawesome-free-solid'
import { faCircle as farCircle } from '@fortawesome/fontawesome-free-regular'

const Circle = ({ filled, onClick }) => {

  return (
    <div onClick={onClick} >
      <FontAwesomeIcon icon={filled ? farCircle : fasCircle}/>
    </div>
  );
};

class App extends React.Component {
  state = { filled: false };

  handleClick = () => {
    this.setState({ filled: !this.state.filled });
  };

  render() {
    return <Circle filled={this.state.filled} onClick={this.handleClick} />;
  }
}

See the github repo for more information: https://github.com/FortAwesome/react-fontawesome

This answer is an edited version of my answer here: How can I get Font Awesome 5 to work with React? .

Since FA 5 injects svg elements into the DOM, it probably doesn't play nicely with React's virtual DOM. Hopefully it's something they'll fix, but a simple workaround is to just include both icons instead of toggling them, and hide one or the other by wrapping it in a container that applies display: none . For example:

renderChatButton() {
  const unread = this.state.unread
  const normalIcon = <i className='far fa-comment' />
  const unreadIcon = <i className='fas fa-comment' />
  return (
    <div className='header-button' onClick={ this.toggleChat }>
      <span className={ unread ? 'hidden' : '' }>{ normalIcon }</span>
      <span className={ unread ? '' : 'hidden' }>{ unreadIcon }</span>
    </div>
  )
}

Looks like another fix is to call window.FontAwesomeConfig = { autoReplaceSvg: 'nest' } somewhere in your app's boot cycle, after the FA JavaScript has been loaded.

See also https://stackoverflow.com/a/48552226/53790 and https://fontawesome.com/how-to-use/svg-with-js#auto-replace-svg-nest .

I faced the same problem today with my ReactJS/Material-UI application. After much research I fixed the issue by using React's key property that forces an element to render when its associated state changes.

So, in this example I would rewrite the render method as below:

render() {
    const icon = this.state.complete ? (<span key={this.state.complete} className="far fa-circle todo-item-icon"/>) : (<span key={this.state.complete} className="far fa-check-circle todo-item-icon"/>);

    return (
      <div className="todo-item" onClick={this.toggleComplete}>
        {icon}
        <span className="todo-item-text">{this.props.item}</span>
      </div>
    );
  }

Here I've used this.state.complete as unique value for the key but you could use anything you like so long as they are unique.

Now, the span element is always removed on re-render and replaced by the other one.

Hope this answer helps future seekers.

根据上面的讨论,这可能是一个很棒的错误,而不是反应错误

You can use to function instead of class but before this you need to downlaod the react fontawesome package for reactjs code:

    import React from  'react';
    import Menuitems from '././Menuitems';
    import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
    import { faTimes, faBars } from '@fortawesome/free-solid-svg-icons';
    
    const Menu =() => {
    
       const [item, setItem]= React.useState(false);
    
       const handleitem =()=>{
         setItem(!true);
       }
        return(
            <>
              <Container>
                  <nav>
                       <h1> react </h1>
                 
                     <div className="menu-icon" onClick={handleitem}>
                        <FontAwesomeIcon icon={item ? faTimes :  faBars } size="3x"/>
                     </div>
                  </nav>
               </Container>
            </>
        );
    }
    
    export default Menu;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM