Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React fontawesome 5 SVG not rerendering

I am using a pro license of Fontawesome 5 with React and I have created a row header component for the header cell in a div table. The cell has Fontawesome generated svgs for the sort icons. I can't get the SVGs to rerender.

Here is my code:

export const row = (value, className, clickHandler) => (
  <div className={`align-self-center text-center ${className ? className : ''}`} onClick={clickHandler}>
    {value}
  </div>
);

export const rowHeader = (value, className, sort, clickHandler) => {
  let iconClass;
  switch (sort) {
    case 'asc':
      iconClass =  'fas fa-sort-up';
      break;
    case 'desc':
      iconClass = 'fas fa-sort-down';
      break;
    default:
      iconClass = 'fal fa-sort';
      break;
  }

  return row(<span>{value} {iconClass} <i className={iconClass + ' float-right'}/></span>, className +  ' clickable', clickHandler);
};

I tried creating the <i .../> as a separate component and that didn't work. The icons render correctly the first time appropriatelys and the iconClass is being changed in my cell. However, the SVG element is not being rerendered.

How do I force the Fontawesome generated SVG icons to rerender when the parent element rerenders?

like image 808
Gremash Avatar asked Dec 08 '17 22:12

Gremash


3 Answers

My solution was to add key={Math.random()} to the SVG's containing element. Then, when it gets new props, it forces a refresh.

like image 198
Matt Saunders Avatar answered Nov 18 '22 13:11

Matt Saunders


This is the solution that finally worked. I was able to find the documentation for using my pro license.

import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import { faSortDown, faSortUp } from '@fortawesome/fontawesome-pro-solid';
import { faSort } from '@fortawesome/fontawesome-pro-light';

export const row = (value, className, clickHandler) => (
  <div className={`align-self-center text-center ${className ? className : ''}`} onClick={clickHandler}>
    {value}
  </div>
);

export const rowHeader = (value, className, sort, clickHandler) => {
  return row(<span>{value} {sortIcon(sort)}</span>, className +  ' clickable', clickHandler);
};

export const sortIcon = (sort) => {
  switch (sort) {
    case 'asc':
      return <FontAwesomeIcon icon={faSortDown} size='lg' className='float-right'/>;
    case 'desc':
      return <FontAwesomeIcon icon={faSortUp} size='lg' className='float-right'/>;
    default:
      return <FontAwesomeIcon icon={faSort} size='lg' className='float-right'/>;
  }
};

Edit - Clarification:

The problem appeared to be that changing the CSS class names on the rendered SVG elements would not cause React to re-render them. Switching to the React Fontawesome library and updating the FontAwesomeIcon component worked.

like image 42
Gremash Avatar answered Nov 18 '22 11:11

Gremash


The reason this doesn't work is because the svg&js flavor of Font Awesome uses a MutationObserver which will replace all icon elements with svg elements.

This means the DOM will be manipulated, which will cause trouble if you're using React to render the DOM. Your icons are not re-rendering, simply because React is no longer able to find them in the DOM because the Font Awesome library has replaced the original element with an svg element. This can lead to other problems as well, see this question: Failed to execute 'removeChild' on 'Node' with FontAwesome in React

To fix this you can use the @fortawesome/react-fontawesome library as you already mentioned in your own answer, or, if you're like me and don't like the verbosity (and having to use a private npm repo, ugh) of this approach, you can simply use the webfonts&css flavor and your problem will be solved without changing your code.

like image 21
René Avatar answered Nov 18 '22 12:11

René