Stripe CarElement not visible in React js - html

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;
}
`;

Related

How to resolve import and export error in reportWebVitals.js

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;
}

React make a full-page Carousel

I am trying to make an animation that scrolls from left to right the child elements of a div, I have looked around but none of the other questions with answers seem to work, so far this is what I have accomplished:
Home.css:
#keyframes scroll {
20% {
transform: translateX(-100vw);
}
40% {
transform: translateX(-200vw);
}
60% {
transform: translateX(-300vw);
}
80% {
transform: translateX(-400vw);
}
100% {
transform: translateX(0vw);
}
}
.section {
display: flex;
flex-direction: row;
font-family: Arial, sans-serif;
font-weight: bold;
transition: 1s;
width: 100vw;
height: 100vh;
animation-name: scroll;
animation-duration: 1s;
}
.child1 {
background-color: #c0392b;
flex: none;
width: 100vw;
height: 100vh;
}
.child2 {
background-color: #e67e22;
flex: none;
width: 100vw;
height: 100vh;
}
.child3 {
background-color: #27ae60;
flex: none;
width: 100vw;
height: 100vh;
}
.child4 {
background-color: #2980b9;
flex: none;
width: 100vw;
height: 100vh;
}
.child5 {
background-color: #8e44ad;
flex: none;
width: 100vw;
height: 100vh;
}
Home.jsx:
import { forwardRef, useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import './Home.css';
const Child1 = forwardRef((props, ref) => {
return <div className="child1">
<h1>Child1</h1>
</div>;
});
const Child2 = forwardRef((props, ref) => {
return <div className="child2">
<h1>Child2</h1>
</div>;
});
const Child3 = forwardRef((props, ref) => {
return <div className="child3">
<h1>Child3</h1>
</div>;
});
const Child4 = forwardRef((props, ref) => {
return <div className="child4">
<h1>Child4</h1>
</div>;
});
const Child5 = forwardRef((props, ref) => {
return <div className="child5">
<h1>Child5</h1>
</div>;
});
function Home() {
let section = useRef();
let [currentSection, setCurrentSection] = useState(0);
useEffect(() => {
window.addEventListener("keypress", (event) => {
if(event.key === "d") {
// scroll right (play animation forwards by one step)
} else if(event.key === "a") {
// scroll left (play animation backwards by one step)
}
});
});
return <div ref={section} className="section">
<Child1></Child1>
<Child2></Child2>
<Child3></Child3>
<Child4></Child4>
<Child5></Child5>
</div>
}
export default Home;
The problem is that the animation plays all at the beginning and I cannot figure out a way to play it only when the keyboard event is triggered, if the event.key is an "a" then the elements should scroll to the left otherwise if the event.key is a "d" then the elements should scroll to the right.
Here is the link to the CodeSandbox.
As I understood you want to make parent scrollable only with pressing keys "a" and "d". I think I have found a solution which would work for you.
My solution:
Removing keyframes and separating it into 5 different classes. So here is CSS file:
.section1 {
transform: translateX(0);
}
.section2 {
transform: translateX(-100vw);
}
.section3 {
transform: translateX(-200vw);
}
.section4 {
transform: translateX(-300vw);
}
.section5 {
transform: translateX(-400vw);
}
.section {
display: flex;
flex-direction: row;
font-family: Arial, sans-serif;
font-weight: bold;
transition: 1s;
width: 100vw;
height: 100vh;
animation-name: scroll;
animation-duration: 1s;
}
.child1 {
background-color: #c0392b;
flex: none;
width: 100vw;
height: 100vh;
}
.child2 {
background-color: #e67e22;
flex: none;
width: 100vw;
height: 100vh;
}
.child3 {
background-color: #27ae60;
flex: none;
width: 100vw;
height: 100vh;
}
.child4 {
background-color: #2980b9;
flex: none;
width: 100vw;
height: 100vh;
}
.child5 {
background-color: #8e44ad;
flex: none;
width: 100vw;
height: 100vh;
}
Now I added some JS behaviour to appropriately 'scroll' left or right. I divided moving left or right into functions, and adding numberOfSections as constant. Here is JS file:
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import "./Home.css";
const Child1 = forwardRef((props, ref) => {
return (
<div className="child1">
<h1>Child1</h1>
</div>
);
});
const Child2 = forwardRef((props, ref) => {
return (
<div className="child2">
<h1>Child2</h1>
</div>
);
});
const Child3 = forwardRef((props, ref) => {
return (
<div className="child3">
<h1>Child3</h1>
</div>
);
});
const Child4 = forwardRef((props, ref) => {
return (
<div className="child4">
<h1>Child4</h1>
</div>
);
});
const Child5 = forwardRef((props, ref) => {
return (
<div className="child5">
<h1>Child5</h1>
</div>
);
});
const numberOfSections = 5;
function Home() {
const section = useRef();
const [currentSection, setCurrentSection] = useState(1);
const moveLeft = () => {
section.current.classList.remove(`section${currentSection}`);
section.current.classList.add(`section${currentSection - 1}`);
setCurrentSection((prevSection) => prevSection - 1);
};
const moveRight = () => {
section.current.classList.remove(`section${currentSection}`);
section.current.classList.add(`section${currentSection + 1}`);
setCurrentSection((prevSection) => prevSection + 1);
};
const changeSection = (event) => {
if (event.key === "d") {
if (currentSection < numberOfSections) {
moveRight();
}
} else if (event.key === "a") {
if (currentSection > 1) {
moveLeft();
}
}
};
useEffect(() => {
window.addEventListener("keypress", changeSection);
return () => window.removeEventListener("keypress", changeSection);
}, [currentSection]);
return (
<div ref={section} className="section">
<Child1></Child1>
<Child2></Child2>
<Child3></Child3>
<Child4></Child4>
<Child5></Child5>
</div>
);
}
export default Home;
I have tested it and it works.
By the way, my suggestions is to wrap some of these functions into useCallback if you plan to expand Home component. And I suggest to use some of npm packages compatible with React for directly manipulating CSS within JS, such as styled-components, which in this case would make it a lot easier to create Carousel, becouse now in order to create more Child components, you need to make new CSS classes.
But this above fixes the given problem. I hope I helped you :)
this solution is tightly cuppled to your example but from my perspective, you could make it more generic.
I didn't use CSS animation instead I used the scrollTo method to achieve scroll behavior.
codesandbox link
Home.jsx
import { forwardRef, useEffect, useRef, useState } from "react";
import "./styles.css";
// I not using forwardRef and ref at all
const Child1 = forwardRef((props, ref) => {
return (
<div className="child1">
<h1>Child1</h1>
</div>
);
});
const Child2 = forwardRef((props, ref) => {
return (
<div className="child2">
<h1>Child2</h1>
</div>
);
});
const Child3 = forwardRef((props, ref) => {
return (
<div className="child3">
<h1>Child3</h1>
</div>
);
});
const Child4 = forwardRef((props, ref) => {
return (
<div className="child4">
<h1>Child4</h1>
</div>
);
});
const Child5 = forwardRef((props, ref) => {
return (
<div className="child5">
<h1>Child5</h1>
</div>
);
});
function App() {
let section = useRef();
let [currentSection, setCurrentSection] = useState(0);
useEffect(() => {
const handler = (event) => {
if (event.key === "d") {
if (currentSection === 4) return;
setCurrentSection((prev) => prev + 1);
} else if (event.key === "a") {
if (currentSection === 0) return;
setCurrentSection((prev) => prev - 1);
}
};
window.addEventListener("keypress", handler);
// you should clean up you EventListener
return () => {
window.removeEventListener("keypress", handler);
};
}, [currentSection]);
useEffect(() => {
window.scrollTo({
behavior: "smooth",
left: window.innerWidth * currentSection,
top: 0,
});
}, [currentSection]);
return (
<div ref={section} className="section">
<Child1></Child1>
<Child2></Child2>
<Child3></Child3>
<Child4></Child4>
<Child5></Child5>
</div>
);
}
export default App;
Home.css
.section {
display: flex;
flex-direction: row;
font-family: Arial, sans-serif;
font-weight: bold;
transition: 1s;
width: 100vw;
height: 100vh;
}
.child1 {
background-color: #c0392b;
flex: none;
width: 100vw;
height: 100vh;
}
.child2 {
background-color: #e67e22;
flex: none;
width: 100vw;
height: 100vh;
}
.child3 {
background-color: #27ae60;
flex: none;
width: 100vw;
height: 100vh;
}
.child4 {
background-color: #2980b9;
flex: none;
width: 100vw;
height: 100vh;
}
.child5 {
background-color: #8e44ad;
flex: none;
width: 100vw;
height: 100vh;
}

canvas.getContext is undefined when generating blurHash Angular 11

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!

Conditional rendering of HTML elements based on boolean value React

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.

Setting image src dynamically in reactJS

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.