LoginSignup
0
0

More than 1 year has passed since last update.

React でアプリを作成しました【14】【Weather app Using Tailwind CSS】

Last updated at Posted at 2022-04-07

環境の準備

①ターミナルでreactアプリケーションを作成する。

$ npx create-react-app <プロジェクト名>
% cd <プロジェクト名>
% npm start

② 必要なパッケージをインストールする。

  1. 公式サイト:Tailwind CSS
$ npm install -D tailwindcss postcss autoprefixer
$ npx tailwindcss init -p
$ npm install moment --save
src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
tailwind.config.js
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

2.公式サイト:Open Weather

API keysを作成する。

3.公式サイト:Moment.js

③ 不必要なファイルを削除する。

App.css
App.test.js
logo.svg
reportWebVitals.js
setupTests.js

コンポーネント・ファイル構成

 src
  ├─ assets //画像を入れる
  ├─ components
       ├── DetailCard.js
       ├── Header.js
       └── SummaryCard.js
  ├── App.js
  ├── index.css
  └── index.js
 ├── .env
 ├── craco.config.js
 ├── tailwind.config.js
.env
REACT_APP_API_KEY = "our API_KEY"
REACT_APP_URL = https://api.openweathermap.org/data/2.5/forecast?
REACT_APP_ICON_URL = http://api/openweathermap.org/img/wn/
public/index.html

// 省略

 <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
    />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>

//省略
src/components/DetailCard.js
import moment from "moment";

function DetailCard({ weather_icon, data }) {
  const { clouds, main, weather } = data.list[0];
  return (
    <div className="container p-4 flex items-center justify-center shadow-lg roundedfd-lg bg-white h-1/3 mb-auto">
      <div className="my-auto">
        <p className="font-bold text-5xl text-pink-800 mb-2">
          {Math.round(main.temp)}&deg;C
        </p>
        <p className="text-4xl text-gray-800 tracking-widest">
          {weather[0].main}
          <img src={weather_icon} className="w-1/4 inline" />
        </p>
        <p className="text-gray-400 text-xs uppercase tracking-widest">
          {weather[0].description}
        </p>
        <p className="tracking-wider">{moment().format("dddd MMM YYYY")}</p>
      </div>
      <div className="my-2 border-l-2 border-gray-100 p-2">
        <p className="text-gray-400 text-lg">
          RealFeel:{Math.round(main.feel_like)}&deg;C
        </p>
        <p className="text-gray-400 text-lg">Humidity:{main.humidity}%</p>
        <p className="text-gray-400 text-lg">Cloud Cover:{clouds.all}%</p>
        <p className="text-gray-400 text-lg">
          Min Temp:{Math.round(main.temp_min)}&deg;C
        </p>
        <p className="text-gray-400 text-lg">
          Max Temp:{Math.round(main.temp_max)}&deg;C
        </p>
      </div>
    </div>
  );
}

export default DetailCard;
src/components/Header.js
const Header = () => {
  return (
    <ul className="flex ml-auto w-full font-bold">
      <li className="text-xs text-gray-800 ml-auto mr-6 border-b-2 border-green-400 cursor-pointer">
        Weather
      </li>
      <li className="text-xs text-gray-800 mr-6 alert-notice cursor-pointer border-b-2 hover:border-green-400">
        Alerts
      </li>
      <li className="text-xs text-gray-800 mr-6 cursor-pointer border-b-2 hover:border-green-400">
        Map
      </li>
      <li className="text-xs text-gray-800 mr-6 cursor-pointer border-b-2 hover:border-green-400">
        Statelite
      </li>
      <li className="text-xs text-gray-800 cursor-pointer border-b-2 hover:border-green-400">
        News
      </li>
    </ul>
  );
};

export default Header;
src/components/SummaryCard.js
import moment from "moment";

function SummaryCard({ day }) {
  let day_icon = `${
    process.env.REACT_APP_ICON_URL + day.weather[0]["icon"]
  }@2x.png`;
  return (
    <li className="container p-4 flexc items-center justify-center bg-gray-200 rounded-lg my-auto mr-1">
      <div className="my-auto">
        <p className="font-bgold text-3xl text-ping-600 mb-2">
          {Math.round(day.main.temp)}&deg;C
        </p>
        <p className="text-2xl text-gray-800 tracking-widest">
          {day.weather[0].main}
          <img src={day_icon} className="w-1/4 inline" />
        </p>
        <p className="text-gray-400 text-xs uppercase tracking-widest">
          {day.weather[0].description}
        </p>
        <p className="tracking-wider">
          {moment(day.dt_txt).format("dddd hh:mm")}am
        </p>
      </div>
    </li>
  );
}

export default SummaryCard;
src/App.js
import { useState } from "react";
import DetailCard from "./components/DetailCard";
import Header from "./components/Header";
import SummaryCard from "./components/SummaryCard";

function App() {
  const API_KEY = process.env.REACT_APP_API_KEY;

  const [noData, setNoData] = useState("No Data Yet");
  const [searchTerm, setSearchTerm] = useState("");
  const [weatherData, setWeatherData] = useState([]);
  const [city, setCity] = useState("Unkown location");
  const [weatherIcon, setWeatherIcon] = useState(
    `${process.env.REACT_APP_ICON_URL}10n@2x.png`
  );

  const handleSubmit = (e) => {
    e.preventDefault();
    getWeather(searchTerm);
  };
  const handleChange = (input) => {
    const { value } = input.target;
    setSearchTerm(value);
  };
  const getWeather = async (location) => {
    setWeatherData([]);

    let how_to_search =
      typeof location === "string"
        ? `q=${location}`
        : `lat=${location[0]}&lon=${location[1]}`;

    try {
      let res = await fetch(
        `${
          process.env.REACT_APP_URL + how_to_search
        }&appid=${API_KEY}&units=metric&cnt=5&exclude=hourly,minutely`
      );
      let data = await res.json();

      if (data.cod !== 200) {
        setNoData("Location Not Found");
        return;
      }
      setWeatherData(data);
      setCity(`${data.city.name},${data.city.country}`);
      setWeatherIcon(
        `${process.env.REACT_APP_ICON_URL + data.list[0]["icon"]}#4x.png`
      );
    } catch (error) {
      console.log("Error encounterd:" + error);
    }
  };

  const myIP = (location) => {
    const { latitude, longitude } = location.coords;
    getWeather([latitude, longitude]);
  };

  return (
    <div className="bg-gray-800 flex items-center justify-center w-screen h-screen py-10">
      <div className="flex w-3/4 min-h-full rounded-3xl shadow-lg m-auto bg-gray-100">
        {/* from card section */}
        <div className="form-container">
          <div className="flex items-center justify-center">
            <h3 className="my-auto mr-auto text-xl text-pink-800 font-bold shadow-md py-1 px-3 rounded-md bg-white bg-opacity-30">
              forecast
            </h3>
            <div className="flex p-2 text-gray-100 bg-gray-600 bg-opacity-30 rounded-lg">
              <i className="fa fa-map my-auto" aria-hidden="true"></i>
              <div className="text-right">
                <p className="font-semibold text-sm ml-2">{city}</p>
              </div>
            </div>
          </div>
          <div className="flex flex-col items-center justify-center h-hull">
            <h1 className="text-white text-2xl">
              The Only Weather Forecast App You Need
            </h1>
            <hr className="h-1 bg-white w-1/4 rounded-full my-5" />
            <form
              noValidate
              onSubmit={handleSubmit}
              className="flex justify-center w-full"
            >
              <input
                type="text"
                className="relative rounded-xl py-2 px-3 w-2/3 bg-gray-300 bg-opacity-60 text-white placeholder-gray-200"
                onChange={handleChange}
                required
              />
              <button type="submit" className="z-10">
                <i
                  className="fa fa-search text-white ml-10 border-l my-auto z-10 cursor-pointer p-3"
                  aria-hidden="true"
                  onClick={() => {
                    navigator.geolocation.getCurrentPosition(myIP);
                  }}
                ></i>
              </button>
            </form>
          </div>
        </div>
        {/*info card section */}
        <div className="w-2/4 p-5">
          <Header />
          <div className="flex flex-col my-10">
            {weatherData.length === 0 ? (
              <div className="container p-4 flex items-center justify-center h-1/3 mb-auto">
                <h1 className="text-gray-300 text-4xl font-bold uppercase">
                  {noData}
                </h1>
              </div>
            ) : (
              <>
                <h1 className="text-5xl text-gray-800 mt-auto mb-4">Today</h1>
                <DetailCard weather_icon={weatherIcon} data={weatherData} />
                <h1 className="text-3xl text-gray-600 mb-4 mt-10">
                  More On{city}
                </h1>
                <ul className="grid grid-cols-2 gap-2">
                  {weatherData.list.map((days, index) => {
                    if (index > 0) {
                      return <SummaryCard key={index} day={days} />;
                    }
                  })}
                </ul>
              </>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;
src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap");

body {
  font-family: "Open sans", sans-serif;
  font-weight: 300;
}

h1 {
  font-weight: bold;
}
.form-container {
  background: url("./assets/blue.jpg") no-repeat 50% 40% / cover;
  height: auto;
  @apply w-2/4 p-5 rounded-3xl;
}
.alert-notice::after {
  content: "";
  width: 5px;
  height: 5px;
  position: absolute;
  border-radius: 50;
  margin-left: 2px;
  margin-top: -3px;
  @apply animate-ping bg-pink-800;
}
src/index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

参考サイト

Build a Weather Forecast App in React and Tailwind for Beginners 2021

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0