簡體   English   中英

如何使用 React 動態渲染圖標 Redux

[英]How to render a icon dynamically using React Redux

我正在創建一個播放列表,我想用不同的圖標顯示我的“最喜歡的曲目”,比如一顆心,默認情況下呈現一個帶邊框的心。 基本上默認代碼是有效的,但是當刷新頁面時,填充的圖標消失並呈現默認值。 如果有人有提示,我是新手,不知道我是否可以有很多這樣的“useStates”。

我忘了說,該應用程序正在使用 Redux-persist,因此信息會在頁面刷新后繼續存在於商店中。 這就是為什么我要顯示根據商店信息填充的圖標

import React, { useState, useEffect } from "react";
import ReactDOMServer from "react-dom/server";
import axios from "axios";
import { useSelector, useDispatch } from "react-redux";
import { ListContainer, PlayerStyle, InfoBox } from "./style.jsx";
import FavoriteBorderIcon from "@material-ui/icons/FavoriteBorder";
import FavoriteIcon from "@material-ui/icons/Favorite";
import PlayCircleOutlineIcon from "@material-ui/icons/PlayCircleOutline";
import PauseCircleOutlineIcon from "@material-ui/icons/PauseCircleOutline";
import MusicPlayer from "../MusicPlayer/index";

const List = () => {
  const [isLoading, setLoading] = useState(true);
  const [valueList, setValueList] = useState(20);
  const [search, setSearch] = useState("");
  const [dataList, setDataList] = useState([]);
  const [trackList, setTrackList] = useState([]);
  const [bannerAlbum, setBannerAlbum] = useState("");
  const [createdBy, setCreatedBy] = useState("");
  const [isPlayed, setIsPlayed] = useState(false);
  const [musicPlayed, setMusicPlayed] = useState("");
  const [showTrackList, setShowTrackList] = useState(true);
  const [showFavoriteList, setShowFavoriteList] = useState(false);
  const [favoriteData, setFavoriteData] = useState([]);

  const favoriteList = useSelector(
    (state) => state.FavoriteListReducer.favorites
  );
  const dispatch = useDispatch();

  let chartPlaylist = `https://api.deezer.com/playlist/1111141961?&limit=${valueList}`;

  useEffect(() => {
    axios.get(chartPlaylist).then((res) => {
      // console.log(res.data, "album");
      setDataList(res.data);
      setTrackList(res.data.tracks.data);
      setBannerAlbum(res.data.picture_medium);
      setCreatedBy(res.data.creator.name);
      // Ao terminar a requisição dos dados, mostra os componentes
      setLoading(false);
    });
  }, [chartPlaylist]);

  useEffect(() => {
    axios.all(favoriteList.map((l) => axios.get(l))).then(
      axios.spread(function (...res) {
        // all requests are now complete
        setFavoriteData(res);
      })
    );
    if (!showTrackList && favoriteList.length === 0) {
      setShowTrackList(true);
      setShowFavoriteList(false);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [favoriteList]);

  const handleInput = (e) => {
    setSearch(e.target.value);
  };

  const handlePlayed = () => {
    if (!isPlayed) {
      setIsPlayed(true);
    } else {
      setIsPlayed(false);
    }
  };
  const handleIconSwap = (e) => {
    e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
      !isPlayed ? <PlayCircleOutlineIcon /> : <PauseCircleOutlineIcon />
    );
  };

  const Add_fav = (e) => {
    dispatch({
      type: "ADD_FAV",
      newId: `https://api.deezer.com/track/${e.currentTarget.getAttribute(
        "value"
      )}`,
    });

    e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
      favoriteList.includes(e.currentTarget.getAttribute("value")) ? (
        ""
      ) : (
        <FavoriteIcon style={{ color: "red" }} />
      )
    );
  };

  const Del_fav = (e) => {
    dispatch({
      type: "DELETE_FAV",
      remove: `https://api.deezer.com/track/${e.currentTarget.getAttribute(
        "value"
      )}`,
    });

    e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
      favoriteList.includes(e.currentTarget.getAttribute("value")) ? (
        ""
      ) : (
        <FavoriteBorderIcon fontSize={"inherit"} />
      )
    );
  };

  // eslint-disable-next-line no-array-constructor
  const handleFav = (e) => {
    favoriteList.includes(
      `https://api.deezer.com/track/${e.currentTarget.getAttribute("value")}`
    )
      ? Del_fav(e)
      : Add_fav(e);
  };

  const toggleData = () => {
    if (showTrackList && favoriteList.length > 0) {
      setShowTrackList(false);
      setShowFavoriteList(true);
    }
    if (!showTrackList) {
      setShowTrackList(true);
      setShowFavoriteList(false);
    }
  };

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <>
      <button
        onClick={() => {
          if (valueList < 100) {
            setValueList(valueList + 20);
          }
        }}
      >
        adicionar +20 musicas
      </button>
      <br />

      <br />
      <br />
      <button
        onClick={() => {
          toggleData();
        }}
      >
        Mostrar Favoritos
      </button>
      <InfoBox>
        <input
          type="text"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
        />
        <div className="content">
          <img src={bannerAlbum} alt="" />
          <div className="Info--desc">
            <h1>{dataList.title}</h1>
            <div>
              <span>criado por: </span>
              {createdBy}
            </div>
          </div>
        </div>
      </InfoBox>
      <ListContainer>
        <div className="headerList">
          <div className="rank">#</div>
          <div className="favorite--icon">
            {/* <FavoriteBorderIcon fontSize={"inherit"} /> */}
          </div>
          <div className="title--music">FAIXA</div>
          <div className="title--artist">ARTISTA</div>
          <div className="title--album">ALBUM</div>
          <div className="title--duration">D.</div>
        </div>

        <div className="bodyList">
          {showTrackList &&
            trackList.map((item, key) => {
              return (
                <>
                  <div
                    key={key}
                    onMouseEnter={handleIconSwap}
                    onMouseLeave={(e) => {
                      if (!isPlayed) {
                        e.currentTarget.firstElementChild.innerHTML = key + 1;
                      } else {
                        e.currentTarget.firstElementChild.innerHTML =
                          ReactDOMServer.renderToString(
                            <PauseCircleOutlineIcon />
                          );
                      }
                    }}
                  >
                    <div
                      className="rank"
                      onClick={() => setMusicPlayed(trackList[key].preview)}
                    >
                      {key + 1}
                    </div>
                    <div className="favorite--icon">
                  // Here that has to dynamically render 
                      <span value={item.id} onClick={(e) => handleFav(e)}>
                        <Icon favIco={false} />
                      </span>
                    </div>
                    <div className="title--music">
                      <a target="_blank" rel="noreferrer" href={item.link}>
                        {item.title}
                      </a>
                    </div>
                    <div className="title--artist">{item.artist.name}</div>
                    <div className="title--album">{item.album.title}</div>
                    <div className="title--duration">
                      {item.duration / 60 < 10
                        ? "0" +
                          (item.duration / 60)
                            .toFixed(2)
                            .toString()
                            .replace(".", ":")
                        : (item.duration / 60)
                            .toFixed(2)
                            .toString()
                            .replace(".", ":")}
                    </div>
                  </div>
                </>
              );
            })}
        </div>
      </ListContainer>
    </>
  );
};

const Icon = (props) => {
  switch (props.favIco) {
    case true:
      return <FavoriteIcon style={{ color: "red" }} />;
    case false:
      return <FavoriteBorderIcon fontSize={"inherit"} />;
    default:
      return <FavoriteBorderIcon fontSize={"inherit"} />;
  }
};

export default List;

我的 Reducer 商店工作正常,只是在加載頁面時不會渲染心臟填充,如果我點擊心臟並從商店中刪除信息並再次單擊圖標再次變為填充。

const initialState = {
  favorites: [],
};

const List = (state = initialState, action) => {
  switch (action.type) {
    case "ADD_FAV":
      return { ...state, favorites: [...state.favorites, action.newId] };
    case "DELETE_FAV":
      let index = state.favorites.indexOf(action.remove);
      if (index > -1) {
        state.favorites.splice(index, 1);
      }
      return {
        ...state,
        favorites: [...state.favorites],
      };
    default:
  }

  return state;
};

export default List;

謝謝你們

我在你上面的評論中看到你目前正在使用 redux-persist 來保持商店在重新加載之間,所以訪問數據不是問題。

查看您的代碼,我認為問題可能出在您呈現圖標的方式上。

如果我理解正確的話,這是由Add_fav function 完成的:

const Add_fav = (e) => {
  dispatch({
    type: "ADD_FAV",
    newId: `https://api.deezer.com/track/${e.currentTarget.getAttribute(
      "value"
    )}`,
  });

  e.currentTarget.firstElementChild.innerHTML = ReactDOMServer.renderToString(
    favoriteList.includes(e.currentTarget.getAttribute("value")) ? (
      ""
    ) : (
      <FavoriteIcon style={{ color: "red" }} />
    )
  );
};

但是這個 function 僅在您添加新收藏夾時運行 - 因此重新加載后沒有圖標,因為這部分代碼根本不會運行。

我的建議是將圖標渲染邏輯更改為組件的返回 JSX,而不是在事件處理程序 function 內部。像這樣:

<span value={item.id} onClick={(e) => handleFav(e)}>
  { 
    favoriteList.includes(item.id) 
    ? <FavoriteIcon style={{ color: "red" }} /> 
    :  <FavoriteBorderIcon fontSize={"inherit"} /> 
  }
</span>

所以發現我被發送到商店的是一個字符串,並且在渲染邏輯中正在搜索一個 int 數字,然后編輯 function 添加方法parseInt()

const Add_fav = (e) => {
  dispatch({
    type: "ADD_FAV",
    newId: parseInt(e.currentTarget.getAttribute("value")),
  });
};

現在工作正常,因為邏輯正在搜索 Number Int。

<span value={item.id} onClick={(e) => handleFav(e)}>
  { 
    favoriteList.includes(item.id) 
    ? <FavoriteIcon style={{ color: "red" }} /> 
    :  <FavoriteBorderIcon fontSize={"inherit"} /> 
  }
</span>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM