I'm wanting to pass a state from my WeatherComponent through to my UserForm component and ideally would like to do this without making the UserForm a child component. At the moment I have the UserForm as a child component and that component is working fine when I render it because it's getting the weather state from the WeatherComponent. But now when I want to render my WeatherComponent it renders the UserComponent as well. Is there another way I could use useContext or a method that doesn't rely on making a child component?
WeatherComponent:
import axios from 'axios';
import { useEffect, useState, createContext } from 'react';
import { Weather } from '../types';
import { MdWbSunny } from 'react-icons/md';
import { IoIosPartlySunny } from 'react-icons/io';
import { BsFillCloudSnowFill } from 'react-icons/bs';
import { Title, Text, Container } from '@mantine/core';
import UserForm from './UserForm';
export const WeatherContext = createContext<any>(null);
const WeatherComponent = () => {
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 (
<Container>
<WeatherContext.Provider value={weather?.forcast}>
<UserForm />
</WeatherContext.Provider>
<Title order={2}>
{weather?.forcast === 'Sunny' ? (
<MdWbSunny data-testid="sunny" />
) : weather?.forcast === 'Snowing' ? (
<BsFillCloudSnowFill data-testid="snowing" />
) : (
<IoIosPartlySunny data-testid="overcast" />
)}
</Title>
<Text size="xl" data-testid="forcast">
{weather?.forcast}
</Text>
<Text size="lg" data-testid="temp">
Temp: {`${weather?.min} to ${weather?.max}`}
</Text>
<Text size="md" data-testid="description">
{weather?.description}
</Text>
</Container>
);
};
export default WeatherComponent;
UserForm:
import React, { useContext, useState } from 'react';
import { Container, Title, TextInput, Button, Group, Header } from '@mantine/core';
import { useStateWithLocalStorage } from './UseStateWithLocalStorage';
import { WeatherContext } from './WeatherComponent';
import { MdWbSunny } from 'react-icons/md';
import { BsFillCloudSnowFill } from 'react-icons/bs';
import { IoIosPartlySunny } from 'react-icons/io';
const UserForm = () => {
const [inputValue, setInputValue] = useStateWithLocalStorage('', 'form');
const [show, setShow] = useState(true);
const weatherIcon = useContext(WeatherContext);
function handleChange(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
setInputValue(() => ({
[event.target.name]: event.target.value,
}));
}
return (
<Header height={56} mb={120}>
<Container
style={{
display: 'flex',
flexDirection: 'row',
backgroundColor: 'gray',
justifyContent: 'space-between',
color: 'white',
alignItems: 'center',
padding: '10px',
fontSize: '25px',
fontWeight: 'bold',
boxShadow: '0 3px 6px 0 #555',
}}
>
<Group>
<Title order={2}>Welcome </Title>
{show && (
<TextInput
type="text"
name="name"
id="name"
placeholder="enter your name"
onChange={handleChange}
value={inputValue.name}
/>
)}
{show && <Button onClick={() => setShow((prev) => !prev)}>SAVE</Button>}
<Title order={2}>{inputValue.name ? inputValue.name : ''}</Title>
</Group>
<Group style={{ display: 'flex', justifyContent: 'flex-end' }}>
<Title order={2}>
{weatherIcon === 'Sunny' ? (
<MdWbSunny data-testid="sunny" />
) : weatherIcon === 'Snowing' ? (
<BsFillCloudSnowFill data-testid="snowing" />
) : (
<IoIosPartlySunny data-testid="overcast" />
)}
</Title>
</Group>
</Container>
</Header>
);
};
export default UserForm;