I am importing data fine at the moment using useContext although I am repeating the name weather.weather twice to get to the specific value. I would like to deconstruct the variable with the data so as to not repeat weather twice while obtaining the data. This is my current export:
import { createContext, useState, useEffect } from 'react';
import { Weather } from '../../types';
import axios from 'axios';
import { WeatherContextType, WeatherContextProviderProps } from '../../types';
export const WeatherContext = createContext<WeatherContextType | null>(null);
export const WeatherContextProvider = ({ children }: WeatherContextProviderProps) => {
const [weather, setWeather] = useState<Weather | null>();
const fetchWeatherData = async () => {
const response = await axios.get('http://mock-api-call/weather/get-weather');
setWeather(response.data.result.weather);
};
useEffect(() => {
fetchWeatherData();
}, []);
return (
<WeatherContext.Provider value={{ weather, setWeather }}>{children}</WeatherContext.Provider>
);
};
and where I am importing the data:
import { WeatherContext } from './context/WeatherContext';
import { Title, Text, Container } from '@mantine/core';
import { useContext } from 'react';
const WeatherComponent = () => {
const weather = useContext(WeatherContext);
console.log(weather);
return (
<Container style={{ width: '100%' }}>
<Title order={2}>Today's Weather</Title>
<Text size="lg">
{weather?.weather?.forcast} with a low of {weather?.weather?.min} and a high of{' '}
{weather?.weather?.max}
</Text>
<Text size="md" data-testid="description">
{weather?.weather?.description}
</Text>
</Container>
);
};
export default WeatherComponent;
I thought I could do something as simple as:
const {weather} = useContext(WeatherContext);
although I then get this error:
Property 'weather' does not exist on type 'WeatherContextType | null'.
As I mentioned the code works I really just want to pretty it up.
WeatherContextType:
export type WeatherContextType = {
weather: Weather | null | undefined;
setWeather: React.Dispatch<React.SetStateAction<Weather | null | undefined>>;
};
CodePudding user response:
First, define your context type with optional values if you haven't already, eg
interface WeatherContextType {
weather?: Weather; // optional
setWeather: Dispatch<SetStateAction<Weather | undefined>>;
}
Then initialise your context with a default value. This will mean you can use fewer null-checks and optional chaining. It also means you can destruct properties from it.
export const WeatherContext = createContext<WeatherContextType>({
setWeather: () => {}
});
In your provider, you can omit the null
union by using the default undefined
initial state value
const [weather, setWeather] = useState<Weather>(); // default undefined
Now you can safely destructure weather
from your context which will either be a Weather
type or undefined
.
const { weather } = useContext(WeatherContext);