Building a Weather App in React with D3

Posted on:|Last Updated:|Category: Experiment

View Live Demo!Featured image for Building a Weather App in React with D3

Introduction:

Crafting a seamless and visually pleasing weather app has always been a compelling challenge to undertake. For this project, I embarked on a journey to create a simple yet sophisticated weather app using React and Tailwind, while pulling real-time weather data from Weatherbit. The goal was not just to display weather information in an aesthetically pleasing manner but also to recreate and bring to life a design I admired online, to test my skills in emulating design accuracy and details.

The Project Overview:

The weather app developed offers a myriad of features allowing users to interact with and obtain weather information seamlessly. The user can search for various locations and instantly receive detailed weather updates. The app displays crucial information like the current temperature, 'feels like' temperature, rainfall, and pressure metrics.

Adjacent to the main weather details, users can view additional information through small, well-organized boxes that display the UV index, wind info, sunrise/sunset timings, humidity levels, visibility, and air quality. To further enhance user interaction, the app provides a comprehensive weekly forecast depicting essential details like date, high and low temperatures, wind speed and direction, and the chance of rain. All these features are harmoniously tied together with two meticulously designed line graphs representing the high and low temperatures.

The Development Process:

1. Challenges:

  • Rate Limits:Using the free API from Weatherbit came with its limitations, particularly the rate limits which posed a significant challenge during development. To combat this, I created mock data that would automatically switch over when the rate limit came into effect, allowing seamless development even when the live data was inaccessible

javascript
function App() {
  const [weatherData, setWeatherData] = useState({ current: null, forecast: null, usingMockData: false });

  const handleLocationSearch = (location) => {
    const searchParams = {};
    if (location.includes(',')) {
      const [city, state, country] = location.split(',').map(param => param.trim());
      searchParams.city = city;
      if (state) searchParams.state = state;
      if (country) searchParams.country = country;
    } else {
      searchParams.city = location.trim();
    }
    fetchData(searchParams);
  }

  const fetchData = async (searchParams) => {
    try {
      const [currentWeather, dailyForecast] = await Promise.all([
        fetchCurrentWeatherData(searchParams),
        fetchDailyForecast(searchParams)
      ]);

      setWeatherData({
        current: currentWeather.data[0],
        forecast: dailyForecast.data,
        usingMockData: false
      });
    } catch (error) {
      setWeatherData({
        current: mockWeatherData.current.data[0],
        forecast: mockWeatherData.forecast.data,
        usingMockData: true
      });
    }
  };

  useEffect(() => {
    fetchData({ lat: 51.5074, lon: -0.1278 });
  }, []);
  • Implementing Charts:The implementation and styling of the charts were another hurdle to overcome. Recreating them in a manner that aligned with my design vision required a meticulous approach and a deep dive into the intricacies of chart drawing and styling.

  • Time Constraint:One of the personal challenges I set for myself was to complete this project swiftly. Balancing quality with speed became a crucial aspect of the development phase.

2. Learning Curve:

Through the ups and downs of this project, my skills and knowledge were significantly honed. I learned how to interact with simple APIs using React and experienced firsthand the complexities of plotting updating charts with d3. Additionally, building from a design spec allowed me to refine my skills in translating design visions into functional, tangible applications. Here's how I manipulated the weekly forecast data into my d3 chart:

javascript
function WeeklyForecast({ data }) {
  const yScale = d3.scaleLinear()
    .domain([d3.min(data, d => d.min_temp) - 2, d3.max(data, d => d.max_temp) + 2])
    .range([150, 0]);

  const createLineGenerator = yValue => d3.line()
    .x((d, i) => i * (700/3))
    .y(d => yScale(d[yValue]))
    .curve(d3.curveMonotoneX);

  return (
    <div className="weeklyforecast-info relative">
      <div className="flex pt-4 px-4 sm:px-10 sm:pt-10 pb-0 mx-auto">
      {data.slice(0, 5).map((day, index) => (
        <Day day={day} isFirst={index === 0} key={day.valid_date} />
      ))}
        <TemperatureLine 
          maxTempPathData={createLineGenerator('max_temp')(data)} 
          minTempPathData={createLineGenerator('min_temp')(data)} 
        />
      </div>
    </div>
  );
}

Then display the data like this:

javascript
function TemperatureLine({ maxTempPathData, minTempPathData }) {
  return (
    <svg style={{ top: "210px" }} className="absolute left-0 w-full" viewBox="0 0 700 150" preserveAspectRatio="none">
      <defs>
        <linearGradient id="maxTempGradient" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stopColor="#f9d728" />
          <stop offset="100%" stopColor="#ff3a66" />
        </linearGradient>
        <linearGradient id="minTempGradient" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%" stopColor="#20d092" />
          <stop offset="100%" stopColor="#2f91ff" />
        </linearGradient>
      </defs>
      <path d={maxTempPathData} fill="none" stroke="url(#maxTempGradient)" strokeWidth="4" />
      <path d={minTempPathData} fill="none" stroke="url(#minTempGradient)" strokeWidth="4" />
    </svg>
  );
}

Future Improvements:

While the project was rewarding and allowed me to reach the objectives set, there is always room for enhancement and expansion. Here are a few things on my list for future improvements:

  • UI Tweaks:

    There are always minor details and adjustments that can be made to refine the user interface.

  • Get Current Location:

    Implementing a feature that allows the app to automatically pull the weather information of the user's current location would enhance usability.

  • Adding More Data & Pages:

    There is a plethora of weather data available. Incorporating more data points and adding more pages can provide users with a more comprehensive overview of the weather.

  • Expansion of Displayed Data:

    I plan on extending the range and type of data displayed, offering users a more detailed and informative experience.

mathematicaCopy code< Insert Mockups or Screenshots of Potential Improvements Here >

Conclusion:

This journey to create a visually appealing and functional weather app was filled with learning and exploration. Facing and overcoming various challenges allowed me to refine my problem-solving skills and further my understanding of front-end development. From handling API rate limits to drawing complex charts and working from a design spec, every step was a valuable lesson in its own right.

As I reflect on this project, I see a path paved with continuous learning and improvement. There are numerous possibilities to enhance and expand the app, and I am excited about the prospects of future development.

Whether you are a fellow developer or a weather enthusiast, I hope my weather app serves as a source of inspiration and sparks curiosity about the endless possibilities in the world of front-end development. Enjoy exploring the app, and stay tuned for more updates and improvements!

Github Repository:weather-app-v1-react

Latest commit: Update theme color in index.htmlView

2
2
0

Tech Stack:

Related Projects

Image for Dialogue Creator

A tool for designing non-linear game dialogues with a visual node-based interface. Features include adding characters, messages, branches, and randomness.

Image for Full Stack Workout Tracker with Next 14, Prisma, & Clerk

Full stack workout tracking app. Browse over 800 exercises, create your own routines, record your workouts and view your data with beautiful charts. Built with Next.js 14, Clerk and OpenAI