在 reactJS 中動態設置圖像 src (Setting image src dynamically in reactJS)


問題描述

在 reactJS 中動態設置圖像 src (Setting image src dynamically in reactJS)

我正在嘗試在 reactJS 中動態設置圖像 src,我使用的是打字稿,關於圖像的代碼如下。

這只是我的代碼的一部分,展示了邏輯我正在嘗試更新我的應用程序中的天氣圖標。

如果您有其他意見,請不要害羞分享它們,我非常樂意接受任何建設性的批評,但主要是請幫助解決我在這裡遇到的主要問題。

  const [Icon, setIcon] = useState("");
 useEffect(() => {
    switch (Icon.substr(0, 2)) {
      case "01":
        setIcon(sun);
        break;
      case "50":
        setIcon(fog);
        break;
      case "09":
        setIcon(drizzle);
        break;
      case "11":
        setIcon(storm);
        break;
      case "09" || "13" || "10":
        setIcon(rain);
        break;
      case "13":
        setIcon(snow);
        break;
      case "02" || "03" || "04":
        setIcon(clouds);
        break;
      default:
        setIcon(sun);
    }
  }, [Icon]);


return(<img src={Icon} />)

‑‑‑完整代碼‑‑‑‑

import React, { useState, useEffect } from "react";
import styled from "styled‑components";
import Header from "../components/Header";
import Footer from "../components/Footer";
import axios from "axios";
import Clock from "react‑live‑clock";
import Loading from "../components/PageLoader";
import rain from "../images/rain.svg";
import drizzle from "../images/drizzle.svg";
import fog from "../images/fog.svg";
import clouds from "../images/cloud.svg";
import snow from "../images/snowflake.svg";
import storm from "../images/storm.svg";
import sun from "../images/sun.svg";

export default function Home() {
  let cityName: string;
  const [city, setCity] = useState("amman");
  const [Icon, setIcon] = useState("");
  const changeC = document.getElementById("C");

  const changeF = document.getElementById("F");
  const [triggerEffect, setTriggerEffect] = useState(0);
  const textChange = (event: any) => {
    cityName = event.target.value;
  };

  const [weatherData, setWeatherData] = useState(Object);

  const time = Date();
  useEffect(() => {
    const fetchData = async () => {
      await axios
        .get(`http://localhost:5000/app/weather?address=${city}`)
        .then((response) => {
          setWeatherData(response.data);
          setIcon(response.data.weather["0"].icon);
        });
    };
    fetchData();
  }, [triggerEffect]);
  const [celsius, setCelsius] = useState(true);
  const onSubmitText = (event: any) => {
    event.preventDefault();
    setTriggerEffect(triggerEffect + 1);
    if (cityName) {
      setCity(cityName);
    }
  };
  function convertCels() {
    setCelsius(true);
    console.log(changeF + " " + changeC);
    if (changeC && changeF) {
      changeC.style.color = "#0000ff";
      changeF.style.color = "000000";
    }
  }
  function convertFeh() {
    setCelsius(false);
    console.log(changeF + " " + changeC);
    if (changeC && changeF) {
      changeC.style.color = "#000000";
      changeF.style.color = "0000ff";
    }
  }
  useEffect(() => {
    switch (Icon.substr(0, 2)) {
      case "01":
        setIcon(sun);
        break;
      case "50":
        setIcon(fog);
        break;
      case "09":
        setIcon(drizzle);
        break;
      case "11":
        setIcon(storm);
        break;
      case "09" || "13" || "10":
        setIcon(rain);
        break;
      case "13":
        setIcon(snow);
        break;
      case "02" || "03" || "04":
        setIcon(clouds);
        break;
      default:
        setIcon(sun);
    }
  }, [Icon]);
  return weatherData["name"] && Icon ? (
    <MainContainer>
      <Header />
      <SearchBoxDiv>
        <Button type="submit" onClick={onSubmitText}></Button>
        <SearchBox
          id="text"
          type="text"
          placeholder="Location"
          onChange={textChange}
        ></SearchBox>
      </SearchBoxDiv>
      <MainMidContainer>
        <SecMidContainer>
          <Vector src={Icon} />
        </SecMidContainer>
        <SecMidContainer>
          <CityName>{weatherData["name"]}</CityName>
          <Time>
            {time.toString().substr(0, 3) + " "}
            <Clock format={"HH:mm"} ticking={true} timezone={"Asia/Amman"} />
          </Time>
          <Condition>{weatherData.weather["0"].description}</Condition>
          <Temp id="temp">
            {celsius
              ? parseInt(weatherData.main["temp"]) ‑ 270 + " C"
              : parseInt(
                  parseInt(weatherData.main["temp"]) * (9 / 5) ‑ 459 + "",
                ) + " F"}
            {"   "}
            <SuperScriptC
              onClick={() => {
                convertCels();
              }}
            >
              C
            </SuperScriptC>
            {"   "}
            <SuperScriptF
              id="F"
              onClick={() => {
                convertFeh();
              }}
            >
              F
            </SuperScriptF>
          </Temp>
        </SecMidContainer>
      </MainMidContainer>
      <Footer />
    </MainContainer>
  ) : (
    <Loading />
  );
}
const MainContainer = styled.div`
  width: 1400px;
  height: 100vh;
  display: flex;
  flex‑direction: column;
  align‑items: center;
  justify‑content: space‑between;
`;

const SearchBoxDiv = styled.form`
  width: 500px;
  height: 55px;
  display: flex;
  align‑self: center;
`;
const SearchBox = styled.input`
  width: 497px;
  height: 54px;
  background: #ffffff;
  border: 1px solid #000000;
  box‑sizing: border‑box;
  padding: 15px;
`;

const MainMidContainer = styled.div`
  width: 900px;
  heigh: 330px;
  display: flex;
  flex‑direction: row;
  justify‑content: space‑between;
  align‑self: center;
`;

const SecMidContainer = styled.div`
  width: 40%;
  height: 330px;
  display: flex;
  flex‑direction: column;
`;
const Vector = styled.img`
  height: 330px;
  width: 330px;
`;
const CityName = styled.p`
  font‑family: Roboto;
  font‑style: normal;
  font‑weight: 900;
  font‑size: 40px;
  line‑height: 47px;
`;
const Time = styled.p`
  font‑family: Roboto;
  font‑style: normal;
  font‑weight: normal;
  font‑size: 40px;
  line‑height: 47px;
`;
const Condition = styled.p`
  font‑family: Roboto;
  font‑style: normal;
  font‑weight: normal;
  font‑size: 30px;
  line‑height: 35px;
`;

const Temp = styled.p`
  font‑family: Roboto;
  font‑style: normal;
  font‑weight: normal;
  font‑size: 100px;
  line‑height: 117px;
  margin‑top: 15 %;
`;
const SuperScriptC = styled.sup`
  font‑family: Roboto;
  font‑style: normal;
  font‑weight: normal;
  font‑size: 40px;
  line‑height: 47px;
`;
const SuperScriptF = styled.sup`
  font‑family: Roboto;
  font‑style: normal;
  font‑weight: normal;
  font‑size: 40px;
  line‑height: 47px;
`;
const Button = styled.input`
  background‑color: transparent;
  color: transparent;
  border: none;
`;


參考解法

方法 1:

Problem: useEffect Dependencies

Your useEffect has Icon as a dependency but it is also setting Icon. So every time that it changes the icon it will run again. There is potential for an infinite loop with this setup. Currently what's happening is that is always ends up on sun from the default case because whenever you set it to something else, it triggers a re‑run with the new value.

Solution 1: Better Dependencies

We want to reset the Icon whenever the weatherData changes. Rather than calling setIcon(response.data.weather["0"].icon) (delete that line), we use the icon from the data to trigger the effect. The data is already stored in state as weatherData.

// you need to fix your initial value of weatherData, but I think this will work
const weatherIcon = weatherData?.weather?.[0]?.icon;

useEffect(() => {
    switch (weatherIcon.substr(0, 2)) {
      case "01":
        setIcon(sun);
        break;
      /* ... */
    }
  }, [weatherIcon]);

Solution 2: Pure Function

We are just choosing an icon based on a string value in the API response. This does not need to be an effect. We can define a function that finds the icon for a given string and call that function ourselves at the appropriate time. This function also saves you from having to write break a bunch of times because we are calling return.

const findIcon = (weatherIcon: string) => {
  switch (weatherIcon.substr(0, 2)) {
      case "01":
        return sun;
      case "50":
        return fog;
      /* ... */
      default:
        return sun;
    }
}

You would call this function in your fetch effect and delete the icon update effect.

.then((response) => {
  setWeatherData(response.data);
  setIcon(findIcon(response.data.weather["0"].icon));
});

There are some other issues with your code, but this should fix the icon situation.

(by Moe AtharbehLinda Paiste)

參考文件

  1. Setting image src dynamically in reactJS (CC BY‑SA 2.5/3.0/4.0)

#react-hooks #styled-components #TypeScript #reactjs #html






相關問題

使用反應鉤子useState更新功能的正確方法是什麼? (What is the correct way to use react hook useState update function?)

測試 react-redux useSelector (testing react-redux useSelector)

從深度嵌套的組件更新狀態而不重新渲染父組件 (Update state from deeply nested component without re-rendering parents)

React Hook useEffect 缺少依賴項(在上下文中定義的函數) (React Hook useEffect has a missing dependency (function defined in context))

類型“IntrinsicAttributes”(自定義掛鉤)上不存在 React 屬性 (React Property does not exist on type 'IntrinsicAttributes' (custom hook))

即使驗證要求無效,數據仍在發送,解決此問題的最佳方法是什麼? (Data is sending even if validation requirements are not valid, whats the best way to approach this?)

如何在使用狀態掛鉤更新狀態時覆蓋具有相同鍵的對象 (How can overwrite objects that have the same key in updating State with state hooks)

在 useEffect 中,調用更新時是否會更新所有變量? (In useEffect, do all variables get updated when an update is called?)

反應鉤子,不會將選中的屬性更改為複選框 (React hooks, does not change the checked property to checkbox)

在 reactJS 中動態設置圖像 src (Setting image src dynamically in reactJS)

如何防止組件在反應中重新渲染? (How to prevent component from re-rendering in react?)

使用 useEffect 和 setInterval 一個接一個地打印一個字母 (print one letter after the other with useEffect and setInterval)







留言討論