Home > Software design >  deconstruct useContext object
deconstruct useContext object

Time:07-21

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&apos;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);
  • Related