Is there a way to change colors on <IconTint /> element from react-icon-tint library dynamically?
I need the color to be changed on isActive state change but looks like it's not working as I expected because it uses canvas element under the hood.
I also tried to rerender the whole element on isActive change like that
{
isActive ? (
<IconTint src={getCustomIcon()} color={icon} />
) : (
<IconTint src={getCustomIcon()} color={iconOnBackground} />
);
}
But it also did not work.
I have looked at the source code, because it is a memoized component, it does not rerender on parent rerender, and because inside the useEffect that is used inside the component, dependencies like color and image src is not included, it does not rerender on prop change, obviously it is a bug, or an unwanted behaviour. I don't know why your way of doing it does not work, because those two IconTint components are two different components!. but you can do it this way:
{isActive && <IconTint src={getCustomIcon()} color='#0000ff' />}
{!isActive && <IconTint src={getCustomIcon()} color='#b43285' />}
I don't think this is performant, the source code is so simple, so you can implement IconTint inside your project and just add dependencies inside useEffect dependency array. Here I provided modified version.
import React, { memo, useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
const IconTint = ({ fallback = <span />, src, color, maxWidth, maxHeight }) => {
const canvasRef = useRef(null);
const [size, setSize] = useState({});
const _scaleImage = (srcWidth, srcHeight, maxWidth, maxHeight) => {
if (maxWidth && maxHeight) {
const ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
return { width: srcWidth * ratio, height: srcHeight * ratio };
}
if ((maxWidth && !maxHeight) || (!maxWidth && maxHeight)) {
throw new Error(
"If you are going to provide width, make sure to provide height as well"
);
}
return { width: srcWidth, height: srcHeight };
};
useEffect(() => {
const canvas = canvasRef.current;
// eslint-disable-next-line no-undef
const pic = new Image();
pic.src = src;
const tintCanvas = document.createElement("canvas");
const tintCtx = tintCanvas.getContext("2d");
const ctx = canvas.getContext("2d");
pic.onload = () => {
const result = _scaleImage(pic.width, pic.height, maxWidth, maxHeight);
setSize(result);
tintCanvas.width = result.width;
tintCanvas.height = result.height;
tintCtx.fillStyle = color;
tintCtx.fillRect(0, 0, result.width, result.height);
tintCtx.globalCompositeOperation = "destination-atop";
tintCtx.drawImage(pic, 0, 0, result.width, result.height);
ctx.globalAlpha = 1;
ctx.drawImage(tintCanvas, 0, 0, result.width, result.height);
};
}, [src,color,maxHeight,maxWidth]);
if (
typeof window !== "undefined" &&
window.document &&
window.document.createElement
) {
return <canvas width={size.width} height={size.height} ref={canvasRef} />;
}
return fallback;
};
IconTint.propTypes = {
src: PropTypes.string.isRequired,
color: PropTypes.string.isRequired,
fallback: PropTypes.node,
maxWidth: PropTypes.number,
maxHeight: PropTypes.number
};
export default memo(IconTint);
now you can use this modified version like this:
<IconTint src={getCustomIcon()} color={isActive ? '#0000ff' : "#b43285"}/>