I'm trying to set up an image src dynamically in reactJS, I'm using typescript and the code is as follows regarding the image.
This is just a segment of my code demonstrating the logic im trying to follow with updating the weather icon in my app.
Please, if you have other comments don't be shy to share them i'll be more than happy to accept any constructive criticism but mainly please help with the main issue I have here.
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} />)
---FULL CODE ----
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;
`;
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.
Related
I am new to coding with React.js and monday.com. I am trying to create a weather app that works with monday.com. I want the app to use the location column on the monday.com board to display the weather data for a specific city. My api seems to work just fine and gives the weather results when I enter a city into my app. The below error occurred once I started to try and get the app to work with the monday.com board.
I have an import/export error. I have checked all my opening and closing brackets but they all seem correct. Please advise how I can fix this problem. Thanks!
Error:
Error in ./src/reportWebVitals.js
Syntax error: C:/Users/E7470/weather-app/src/reportWebVitals.js: 'import' and 'export' may only appear at the top level (3:4)
1 |
const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
> 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
| ^
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
# ./src/index.js 19:23-51
App.js:
import React, { useState } from 'react';
import './App.css';
import mondaySdk from 'monday-sdk-js';
import 'monday-ui-react-core/dist/main.css';
//Explore more Monday React Components here: https://style.monday.com/
import AttentionBox from 'monday-ui-react-core/dist/AttentionBox.js';
const monday = mondaySdk();
function App() {
const apiKey = 'API KEY'; //I inserted my api key here
const [weatherData, setWeatherData] = useState([{}]);
const [city, setCity] = useState('');
const [context, setContext] = useState();
const [weatherArr, setWeatherArr] = useState();
useEffect(() => {
const getContext = async () => {
try {
monday.listen('context', (res) => {
console.log(res.data);
getBoards(res.data);
setContext(res.data);
});
monday.listen("settings", (res) => {
console.log(res.data);
setMeasurementUnit(res.data.dropdown)
})
//board id (Add board Id Here eg.328567743)
} catch (err) {
throw new Error(err);
}
};
getContext();
}, []);
const getBoards = async (context) => {
try {
var arr = [];
var boards = [];
for( const boardId of context.boardIds){
var res = await monday.api(`query { boards(limit:1, ids:[${boardId}]) {
name
items {
name
column_values {
id
value
text
}
}
}
}`);
boards.push(res.data.boards);
}
for (const board of boards ){
console.log(boards)
console.log(board)
for (const item of board[0].items){
var location = item.column_values[0].text;
var latLng = item.column_values[0].value
var itemObj = {
location,
latLng
}
const getWeather = (event) => {
if (event.key == 'Enter') {
fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=imperil&APPID=${apiKey}` //api given in video tutorial
)
.then((response) => response.json())
.then((data) => {
setWeatherData(data);
setCity('');
});
}
};
setWeatherArr(arr);
} catch (err) {
throw new Error(err);
}
};
return (
<div className="container">
<input
className="input"
placeholder="Enter City..."
onChange={(e) => setCity(e.target.value)}
value={city}
onKeyPress={getWeather}
/>
{typeof weatherData.main === 'undefined' ? (
<div>
<p>Welcome to weather app! Enter the city you want the weather of.</p>
</div>
) : (
<div className="weather-data">
<p className="city">{weatherData.name}</p>
<p className="temp">{Math.round(weatherData.main.temp)}℉</p>
<p className="weather">{weatherData.weather[0].main}</p>
</div>
)}
{weatherData.cod === '404' ? <p>City not found.</p> : <></>}
</div>
);
}
export default App;
App.css:
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 25px;
}
.input {
padding: 15px;
width: 80%;
margin: auto;
border: 1px solid lightgrey;
border-radius: 6px;
font-size: 16px;
}
.input:focus{
outline: none;
}
.weather-data{
margin-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.city{
font-size: 30px;
font-weight: 200;
}
.temp{
font-size: 90px;
padding: 10px;
border: 1px solid lightgray;
border-radius: 12px;
}
.weather{
font-size: 30px;
font-weight: 200;
}
I am trying to make an app that will display the weather data after the user enters a city. I am using the openweathermap.org API. In my console under networks it shows that I am making an API call when I enter the city but no data is returning. The missing data that should display is the city name, the temperature and the weather. Please advise on how I can fix this.
App.js:
import React, { useState } from 'react';
import './App.css';
function App() {
const apiKey = 'API Key'; //I entered my API key here
const [weatherData, setWeatherData] = useState([{}]);
const [city, setCity] = useState('');
const getWeather = (event) => {
if (event.key == 'Enter') {
fetch(
'https://api.openweathermap.org/data/2.5/weather?q=s{city}&units=imperil&APPID=${apiKey}' //I copied this api from the tutorial
)
.then((response) => response.json())
.then((data) => {
setWeatherData(data);
setCity('');
});
}
};
return (
<div className="container">
<input
className="input"
placeholder="Enter City..."
onChange={(e) => setCity(e.target.value)}
value={city}
onKeyPress={getWeather}
/>
{typeof weatherData.main === 'undefined' ? (
<div>
<p>Welcome to weather app! Enter the city you want the weather of.</p>
</div>
) : (
<div className="weather-data">
<p className="city">{weatherData.name}</p>
<p className="temp">{Math.round(weatherData.main.temp)}℉</p>
<p className="weather">{weatherData.weather[0].main}</p>
</div>
)}
{weatherData.cod === '404' ? <p>City not found.</p> : <></>}
</div>
);
}
export default App;
App.css:
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 25px;
}
.input {
padding: 15px;
width: 80%;
margin: auto;
border: 1px solid lightgrey;
border-radius: 6px;
font-size: 16px;
}
.input:focus{
outline: none;
}
.weather-data{
margin-top: 100px;
display: flex;
flex-direction: column;
align-items: center;
}
.city{
font-size: 30px;
font-weight: 200;
}
.temp{
font-size: 90px;
padding: 10px;
border: 1px solid lightgray;
border-radius: 12px;
}
.weather{
font-size: 30px;
font-weight: 200;
}
In fetch function, you're actually inputting a normal string as a parameter.
Instead of the straight tick like in a normal string (') use backticks (`) instead, and use the dollar sign (${apiKey}) to wrap the variable as a string.
Have you made a error here note the "s" should be a $
https://api.openweathermap.org/data/2.5/weather?q=s{city}&units=imperil&APPID=${apiKey}
https://api.openweathermap.org/data/2.5/weather?q=${city}&units=imperil&APPID=${apiKey}
Below is my code for a checkout page where i have the CardElement and Iam processing the payment through stripe. It was working fine 2 days back but it has disappeared from the UI all of a sudden. enter image description hereIs there any changes Stripe has made in its CardElement package or Am I missing anything in my code?
import React, { useEffect, useState } from "react";
import Header from "./Header";
import styled from "styled-components";
import { useSelector, useDispatch } from "react-redux";
import { selectUserName, selectUserEmail } from "../features/userSlice";
import {
selectCart,
selectCartTotalAmount,
removeAllItems,
} from "../features/cartSlice";
import Orders from "./Orders";
import { CardElement, useStripe, useElements } from "#stripe/react-stripe-js";
import { useHistory } from "react-router-dom";
import axios from "../axios";
import { db } from "../firebase";
function Checkout() {
const userName = useSelector(selectUserName);
const userEmail = useSelector(selectUserEmail);
const cart = useSelector(selectCart);
const cartTotalAmount = useSelector(selectCartTotalAmount);
const history = useHistory();
const [succeeded, setSucceeded] = useState(false);
const [required, setRequired] = useState("");
const [msg, setMsg] = useState("");
const [processing, setProcessing] = useState("");
const [address, setAddress] = useState("");
const [error, setError] = useState(null);
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState(true);
const stripe = useStripe();
const elements = useElements();
const dispatch = useDispatch();
console.log("email", userEmail);
const handlechange = (e) => {
setAddress(e.target.value);
// else {
// setRequired(false);
// }
//localStorage.setItem("address", address);
};
// const goToPreview = () => {
// return <Preview address={address} />;
// };
const validateAddress = () => {};
useEffect(() => {
const getClientSecret = async () => {
const response = await axios({
method: "post",
url: `/payments/create?total=${cartTotalAmount * 100}`,
});
setClientSecret(response.data.clientSecret);
};
getClientSecret();
}, [cart]);
console.log("secret is", clientSecret);
const handleSubmit = async (e) => {
e.preventDefault();
if (address === "") {
setRequired(true);
setMsg("*Address is required!");
} else {
setProcessing(true);
console.log(processing);
const payload = await stripe
.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
},
})
.then(({ paymentIntent }) => {
db.collection("users")
.doc(userEmail)
.collection("orders")
.doc(paymentIntent.id)
.set({
cart: cart,
amount: paymentIntent.amount,
created: paymentIntent.created,
address: address,
});
setSucceeded(true);
setError(null);
setProcessing(false);
dispatch(removeAllItems());
history.replace("/orders");
});
}
};
const handleCardChange = (e) => {
setDisabled(e.empty);
setError(e.error ? e.error.message : "");
};
return (
<div>
<Header />
<Container>
<Name>
<p>Name : {userName}</p>
</Name>
<Email>
<p>Email : {userEmail}</p>
</Email>
<Address>
<p>Address : </p>
<input type="text" onChange={handlechange} />
</Address>
<small>{msg}</small>
<Order>
<p>Order : </p>
</Order>
{cart.map((item) => (
<OrderCard>
<img src={item.image} />
<p>{item.title}</p>
<small>x</small>
<p>{item.cartQuantity}</p>
</OrderCard>
))}
<Payment>
Payment method :
<form>
<CardElement onChange={handleCardChange} />
</form>
</Payment>
<Total>Total : ${cartTotalAmount}</Total>
<button
onClick={handleSubmit}
disabled={processing || disabled || succeeded}
>
{processing ? "Processing" : "Place your order"}
</button>
{error && <div>{error}</div>}
</Container>
</div>
);
}
export default Checkout;
const Container = styled.div`
padding-top: 70px;
width: 100%;
height: 100vh;
background: linear-gradient(to bottom right, #feedf6, #fcf0e2);
margin: auto;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
button {
margin-top: 30px;
margin-bottom: 30px;
margin-left: 40rem;
align-items: center;
}
small {
margin-top: 5px;
margin-left: 20rem;
}
`;
const Name = styled.div`
margin-left: 35rem;
/* margin-right: 10px;
margin-left: 10px; */
`;
const Email = styled.div`
margin-left: 35rem;
/* margin-right: 10px;
margin-left: 10px; */
`;
const Address = styled.div`
display: flex;
justify-content: center;
margin-left: 35rem;
h3 {
margin-right: 10px;
margin-left: 5rem;
}
input {
margin-top: 20px;
margin-left: 5px;
width: 300px;
height: 100px;
}
`;
const Order = styled.div`
margin-left: 35rem;
`;
const OrderCard = styled.div`
display: flex;
justify-content: center;
margin-left: 35rem;
img {
width: 40px;
height: 40px;
}
small {
margin-top: 17px;
margin-left: 10px;
}
p {
margin-left: 20px;
}
`;
const Total = styled.div`
margin-left: 35rem;
`;
const Payment = styled.div`
margin-left: 35rem;
margin-bottom: 20px;
form {
margin-top: 10px;
flex: 0.8;
width: 600px;
height: 40px;
}
`;
I am trying to show blur hash on my images in my angular application before they fully load. But I'm getting the canvas context as undefined inside my function. I can't figure out where the problem is occurring.
I am using the blurhash package.
My code is as follows:
#ViewChild('imageCanvas', {static: false}) imageCanvas: ElementRef<HTMLCanvasElement>;
private context: CanvasRenderingContext2D;
ngAfterViewInit() {
this.getLatestClubPosts()
}
getLatestClubPosts() {
let tempPosts = [];
this._postService
.getClubPosts("Club", 0, 15).pipe(takeUntil(this.destroy$))
.subscribe((res: ApiResponse<any>) => {
res.data.map((singleClubPost: Post, idx, self) => {
this._postService
.getPostCommentsAndReactions(singleClubPost.id, 0, 4)
.subscribe((reactionsAndComments: ApiResponse<any>) => {
singleClubPost.reactionCount =
reactionsAndComments.data.count.reactionCount;
singleClubPost.commentsCount =
reactionsAndComments.data.count.commentsCount;
singleClubPost.reactions = reactionsAndComments.data.reaction;
if(singleClubPost.captureFileURL !== '') {
singleClubPost.media.forEach((image, i) => {
const validRes = isBlurhashValid(image.blurHash);
if(validRes.result == true) {
this.cf.detectChanges();
const blurhashPixels = decode(image.blurHash, 200, 200);
this.context = this.imageCanvas?.nativeElement?.getContext("2d"); //where the error occurs
const imageData = this.context?.createImageData(200, 200);
if (!imageData) {
this.toast.error('Could not prepare Blurhash canvas', 'Error');
}
else {
imageData?.data.set(blurhashPixels)
}
}
})
}
tempPosts.push(singleClubPost);
if (idx == self.length - 1) {
tempPosts.sort(function compare(a, b) {
const dateA = new Date(a.postedDate) as any;
const dateB = new Date(b.postedDate) as any;
return dateB - dateA;
});
this.recentClubPosts = tempPosts;
console.log(this.recentClubPosts)
this.cf.detectChanges();
}
});
});
});
}
My HTML is as follows:
<ng-container *ngFor="let media of post.media; let i = index;">
<img *ngIf="media.blurHash === undefined" [src]="media.captureFileURL"
style="
flex-grow: 1;
flex-direction: row;
flex-wrap: wrap;
display: block;
padding: 0.2em;
box-sizing: border-box;
width: 50%;
height: 400px;
object-fit: cover;
border-radius: 12px;
" />
<canvas #imageCanvas *ngIf="media.blurHash !== undefined" style="
flex-grow: 1;
flex-direction: row;
flex-wrap: wrap;
display: block;
padding: 0.2em;
box-sizing: border-box;
width: 50%;
height: 400px;
object-fit: cover;
border-radius: 12px;">
</canvas>
</ng-container>
Even when I am calling this function inside AfterViewInit it still gives the same error: this.context is undefined. I am really stuck and any help is greatly appreciated. Thanks!
building a component that takes a value (attribute.readonly), and only displays the first 40 characters, unless the <Chevron onClick is triggered which means I then want to show fullDescription as opposed to the shortHeading - ideally this would render similar to how it does currently only doubling the height of the Accordion for example, to allow the rest of the content to fit. I know I'm still a bit off but some pointers for my next steps/ ideas for improving what I have already would be really appreciated!
// #flow
import styled from "styled-components";
import chevron from "../Assets/chevron-icon.svg";
type Props = { attribute: AttributeType, className?: string };
const Accordion = styled.div`
background-color: #e5e9eb;
height: 56px;
width: 612px;
border-radius: 2px;
border: 1px solid #27282a;
margin-bottom: 48px;
display: flex;
align-items: center;
span {
padding-left: 24px;
}
`;
const Chevron = styled.img`
height: 40px;
width: 40px;
float: right;
margin-right: 12px;
`;
const ExpandableString = ({ attribute, className }: Props) => {
const fullDescription = attribute.readonlyvalue;
const shortHeading = fullDescription.substring(0, 40) + '...';
const isExpanded = false;
function toggleContent() {
isExpanded = true;
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
export default ExpandableString;
Put your data in the state and toggle it
const ExpandableString = ({ attribute, className }: Props) => {
const [isExpanded, setIsExpanded] = React.useState(false);
function toggleContent() {
setIsExpanded(prev => !prev);
}
return (
<Accordion className={className}>
<span>{shortHeading}</span>
<Chevron onClick={toggleContent} src={chevron} alt="Expand or collapse content" />
</Accordion>
);
};
Then, according to the isExpanded state, you could conditionally render your React component.