moved passed down states and setters to context

This commit is contained in:
ChrQR 2024-05-09 23:40:14 +02:00
parent 1772c0b543
commit 55b425c03a
8 changed files with 190 additions and 84 deletions

View File

@ -1,5 +1,5 @@
'use server' 'use server'
import { Forecast, LocationType, coordType } from "@/types/types"; import { Forecast, HourlyForecast, LocationType, coordType } from "@/types/types";
//takes address and returns coords in obj as {lat: number, lng: number} //takes address and returns coords in obj as {lat: number, lng: number}
export async function getLocation(searchLocation: string): Promise<coordType>{ export async function getLocation(searchLocation: string): Promise<coordType>{
@ -26,3 +26,16 @@ export async function getForecast(geoLocation: coordType): Promise<Forecast> {
const data: Forecast = await res.json(); const data: Forecast = await res.json();
return data; return data;
} }
export async function getHourlyForecast(geoLocation: coordType): Promise<HourlyForecast> {
const { lat, lng } = geoLocation;
const appId = process.env.WEATHER_API;
const res = await fetch(
`https://pro.openweathermap.org/data/2.5/forecast/hourly?lat=${lat}&lon=${lng}&appid=${appId}&mode=json&`
);
if (!res.ok) {
throw new Error(`Failed to fetch the weather data`);
}
const data: HourlyForecast = await res.json();
return data;
}

View File

@ -1,3 +1,5 @@
import { coordType } from "@/types/types";
export const defaultState = { export const defaultState = {
results: [ results: [
{ {
@ -93,3 +95,8 @@ export const defaultForecast = {
name: "Copenhagen municipality", name: "Copenhagen municipality",
cod: 200, cod: 200,
}; };
export const defaultGeoLocation: coordType = {
lat: 55.647229603577124,
lng: 12.54995987788925
}

View File

@ -1,27 +1,34 @@
'use client' 'use client'
import LocationSearch from '@/components/LocationSearch'; import LocationSearch from '@/components/LocationSearch';
import WeatherNow from '../components/WeatherNow' import WeatherNow from '../components/WeatherNow'
import { useState } from 'react'; import { createContext, useState } from 'react';
import { defaultState } from './defaultState'; import { defaultGeoLocation } from './defaultState';
import { coordType } from '@/types/types'; import { LocationContextType, coordType } from '@/types/types';
import WeatherIcon from '@/components/WeatherIcon'; import WeatherIcon from '@/components/WeatherIcon';
const defaultGeoLocation: coordType = {
lat: 55.647229603577124, export const LocationContext = createContext<LocationContextType>({
lng: 12.54995987788925 geoLocation: defaultGeoLocation,
} setGeoLocation: () => {} // Default function, does nothing
});
export default function Home() { export default function Home() {
const [geoLocation, setGeoLocation] = useState({ const [geoLocation, setGeoLocation] = useState<coordType>(defaultGeoLocation);
lat: 55.647229603577124,
lng: 12.54995987788925 // Create an object that will be passed to the context provider
}); const contextValue: LocationContextType = {
geoLocation,
setGeoLocation
};
return ( return (
<main> <main>
<div className='block mx-auto max-w-4xl'> <div className='block mx-auto max-w-4xl'>
<LocationSearch setGeoLocation={setGeoLocation}/> <LocationContext.Provider value={contextValue}>
<WeatherIcon /> <LocationSearch />
<WeatherNow geoLocation={geoLocation}/> <WeatherIcon />
<WeatherNow />
</LocationContext.Provider>
</div> </div>
</main> </main>
); );

11
components/HourlyCard.tsx Normal file
View File

@ -0,0 +1,11 @@
import { LocationContext } from "@/app/page"
import { useContext } from "react"
export default function HourlyCard() {
const { geoLocation } = useContext(LocationContext)
return (
<h1>Hejsa</h1>
)
};

View File

@ -1,43 +1,47 @@
"use client"; "use client";
import React, { useContext, useState } from "react";
import { getLocation } from "@/app/actions"; import { getLocation } from "@/app/actions";
import { useState } from "react"; import { LocationContext } from "@/app/page";
import { useFormStatus } from "react-dom";
export default function LocationSearch(props: any) {
export default function LocationSearch() {
const [searchLocation, setSearchLocation] = useState(""); const [searchLocation, setSearchLocation] = useState("");
const setGeoLocation = props.setGeoLocation; const { geoLocation, setGeoLocation } = useContext(LocationContext);
const { pending } = useFormStatus(); const [pending, setPending] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchLocation(e.target.value); setSearchLocation(e.target.value);
}; };
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
setPending(true);
try { try {
const coordinates = await getLocation(searchLocation); const coordinates = await getLocation(searchLocation);
setGeoLocation(coordinates); setGeoLocation(coordinates);
} catch (error) { } catch (error) {
console.error("Error fetching location:", error); console.error("Error fetching location:", error);
} }
setPending(false);
}; };
return ( return (
<> <form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit}> <input
<input id="location"
id="location" name="locationSearch"
name="locationSearch" type="text"
type="text" placeholder="Enter a location to get the weather"
placeholder="enter a location to get the weather" value={searchLocation}
value={searchLocation} onChange={handleChange}
onChange={handleChange} disabled={pending}
/> />
<button type="submit" disabled={pending}> <button type="submit" disabled={pending}>
{pending ? "Searching..." : "Search"} {pending ? "Searching..." : "Search"}
</button> </button>
<label htmlFor="location" className="hidden"> <label htmlFor="location" className="hidden">
Enter location to get the weather! Enter location to get the weather!
</label> </label>
</form> </form>
</>
); );
} }

View File

@ -1,5 +1,6 @@
import Image from "next/image"; import Image from "next/image";
import sunnyColor from '../public/sunny.svg' import sunnyColor from '../public/sunny.svg'
import { useContext } from "react";
export default function WeatherIcon() { export default function WeatherIcon() {
return( return(

View File

@ -3,15 +3,18 @@
import { getForecast } from "@/app/actions"; import { getForecast } from "@/app/actions";
import Temperature from "./Temperature"; import Temperature from "./Temperature";
import { Forecast, coordType } from "@/types/types"; import { Forecast, coordType } from "@/types/types";
import { useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { defaultForecast } from "@/app/defaultState"; import { defaultForecast } from "@/app/defaultState";
import { LocationContext } from "@/app/page";
export default function WeatherNow(props: { geoLocation: coordType }) {
// const weather = getForecast(props.geoLocation);
export default function WeatherNow() {
const { geoLocation } = useContext(LocationContext)
const [weather, setWeather] = useState<Forecast>(defaultForecast); const [weather, setWeather] = useState<Forecast>(defaultForecast);
useEffect(() => { useEffect(() => {
let mounted = true; let mounted = true;
getForecast(props.geoLocation).then((data) => { getForecast(geoLocation).then((data) => {
if (mounted) { if (mounted) {
setWeather(data); setWeather(data);
} }
@ -19,12 +22,11 @@ export default function WeatherNow(props: { geoLocation: coordType }) {
return () => { return () => {
mounted = false; mounted = false;
}; };
}, [props.geoLocation]); }, [geoLocation]);
return ( return (
<> <>
<h1>Here is the current weather in {weather.name}</h1> <h1>Here is the current weather in {weather.name}</h1>
<Temperature tempInfo={weather?.main} /> <Temperature tempInfo={weather?.main} />
<p></p>
</> </>
); );
} }

View File

@ -40,53 +40,108 @@ export interface Forecast {
cod: number; cod: number;
} }
export interface HourlyForecast {
cod: string;
message: number;
cnt: number;
list: Array<{
dt: number;
main: {
temp: number;
feels_like: number;
temp_min: number;
temp_max: number;
pressure: number;
sea_level: number;
grnd_level: number;
humidity: number;
temp_kf: number;
};
weather: Array<{
id: number;
main: string;
description: string;
icon: string;
}>;
clouds: {
all: number;
};
wind: {
speed: number;
deg: number;
gust: number;
};
visibility: number;
pop: number;
rain?: {
"1h": number;
};
sys: {
pod: string;
};
dt_txt: string;
}>;
city: {
id: number;
name: string;
coord: {
lat: number;
lon: number;
};
country: string;
population: number;
timezone: number;
sunrise: number;
sunset: number;
};
}
export interface LocationType { export interface LocationType {
results: { results: {
address_components: { address_components: {
long_name: string; long_name: string;
short_name: string; short_name: string;
types: string[];
}[];
formatted_address: string;
geometry: {
bounds: {
northeast: {
lat: number;
lng: number;
};
southwest: {
lat: number;
lng: number;
};
};
location: {
lat: number;
lng: number;
};
location_type: string;
viewport: {
northeast: {
lat: number;
lng: number;
};
southwest: {
lat: number;
lng: number;
};
};
};
place_id: string;
types: string[]; types: string[];
}[];
formatted_address: string;
geometry: {
bounds: {
northeast: {
lat: number;
lng: number;
};
southwest: {
lat: number;
lng: number;
};
};
location: {
lat: number;
lng: number;
};
location_type: string;
viewport: {
northeast: {
lat: number;
lng: number;
};
southwest: {
lat: number;
lng: number;
};
};
};
place_id: string;
types: string[];
}[]; }[];
status: string; status: string;
} }
export interface coordType { export interface coordType {
lat: number; lat: number;
lng: number; lng: number;
} }
export type EffectCallback = () => (void | (() => void | undefined)); export type EffectCallback = () => void | (() => void | undefined);
export interface TempInfo { export interface TempInfo {
temp: number; temp: number;
@ -96,3 +151,9 @@ export interface TempInfo {
pressure: number; pressure: number;
humidity: number; humidity: number;
} }
export interface LocationContextType {
geoLocation: coordType;
setGeoLocation: React.Dispatch<React.SetStateAction<coordType>>;
}