1

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;
goatstash
  • 37
  • 4
  • What do you mean by not making UserForm a child component? React application is a tree of components, thus there is a root and children. Context has to be defined in the parent and then used by child, doesn't matter what level the child is in. If you want to have UserForm a sibling, you have to define context in the shared parent and then set the value of the context in WeatherComponent. But in this case I don't see a value in context. You can better achieve it with state manager, like redux or mobx. – Yaroslav Basovskyy Jul 07 '22 at 01:32

0 Answers0