根据“喜欢”状态更改 SVG 图标颜色

问题描述

我的 React/TypeScript 应用程序中有一个 UpVote 组件,允许用户对帖子进行投票,或删除他们的投票。 upVote 本身是来自 react-icons 包的图标,特别是来自 grommet-Icons 部分。当用户通过点击图标为帖子点赞时,我会像这样动态更改图标的填充:

e.target.querySelector("path").setAttribute("fill","green");

e.target一个 SVG 元素。在该元素中,我使用 querySelector 查找其 path 子元素。在 path 子节点上,我将其“填充”属性设置为绿色。

这很好用,但我想扩展它。当我呈现每个帖子时,我会检查登录用户是否已经对该特定帖子进行了投票。我想根据用户是否已经对帖子进行投票的布尔结果来更改图标的颜色,这样图标就可以已经是绿色的,用户将看到他们已经对帖子进行了投票。我的问题是如何在没有 e.target 的情况下访问“填充”属性 - 因为用户页面加载时没有点击任何内容。我想我的问题非常特定于 react-icons 包的使用,因为当我向返回函数添加图标时,它被包装器组件包装,因此我无法访问 path 元素(在本例中,组件是 GrSign)。

这是上下文的 UpVote 组件:

import React,{ useEffect,useState } from "react";
import { GrSign } from "react-icons/gr";
import { useSelector } from "react-redux";
import { InitialState } from "../store/reducers/rootReducer";

export const UpVote = (props: any) => {

const userState = useSelector((state: InitialState) => {
    return state.auth;
  });

const [upVoted,setUpVoted] = useState(false);

  useEffect(() => {
    if (userState && userState.user !== undefined) {
      const userId = userState.user.user._id;
      if (props.upVotes.indexOf(userId) > -1) {
        // The user already upVoted this post
        setUpVoted(true);
      }
    }

    if (upVoted) {
        
    }
  },[]);

const handleClick = (e: any) => {
    console.log(e.target);
    e.target.querySelector("path").setAttribute("fill","red");
  };
 
  return (
    <div className="flex items-center">
      <GrSign
        size="2em"
        className="transform transition duration-300 hover:scale-125"
        onClick={handleClick}
      />
      <div className="pl-2 text-lg">{props.upVotes.length}</div>
    </div>
  );
};

useEffect 函数中,我想添加一行,如果 upVoted === true 将更改图标的颜色。

为了以防万一,这就是我在 console.log(e.target) 函数中写入 handleClick 时打印到控制台的内容

<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" class="transform transition duration-300 hover:scale-125" height="2em" width="2em" xmlns="http://www.w3.org/2000/svg"><path fill="red" stroke="#000" stroke-width="2" d="M8,23 C10,23 12.9996892,23 15,23 C18,23 19,21 19,18 L19,6 C19,4.00000008 18,3.99999992 17.5,4 C17,4.00000008 15.9998779,5.99999984 L15.9998779,13 C15.9998777,12 16.0001888,10.9999999 14.5003109,10.9999999 C13.000433,10.9999999 13,13 13,13 C13,12 13,11 11.5,10.9999999 C10,10.9999999 10,12 10,12.9999999 L10,4 C10,3 10.029402,2 8.5,2 C7,2 7,3 7,4 L7,18 L7,14 C7,13 6.44999986,12 5.00000005,12 C5,12 4,12 C4,12 4.00000001,14.0384045 4,18 C3.99999999,21.9615955 6,23.023861 8,23 Z"></path></svg>

解决方法

您可以为图标容器创建一个 ref。还添加一个新的 useEffect 块,每次 upvoted 属性更改时都会执行该块:

export const Upvote = (props: any) => {
      //...
      const [upvoted,setUpvoted] = useState(false);
      const iconRef = useRef(null);
    
      useEffect(() => {
        if (userState && userState.user !== undefined) {
          const userId = userState.user.user._id;
          if (props.upvotes.indexOf(userId) > -1) {
            // The user already upvoted this post
            setUpvoted(true);
          }
        }
      },[]);
    
      useEffect(() => {
        if (upvoted) {
             iconRef.current.querySelector("svg path").setAttribute("fill","red")
        }
      },[upvoted]);
    
      const handleClick = e => {
        //...
      };
      return (
        <div className="flex items-center" ref={iconRef}>
          <GrSign
            size="2em"
            className="transform transition duration-300 hover:scale-125"
            onClick={handleClick}
          />
          <div className="pl-2 text-lg">{upvotes.length}</div>
        </div>
      );
    }