如何重构我的代码,以便只在需要时渲染h2标题?

huangapple go评论60阅读模式
英文:

How to refactor my code, so the h2 header renders only when needed?

问题

以下是您要的代码部分的中文翻译:

import { DocumentData, onSnapshot, QuerySnapshot } from "firebase/firestore";
import { useEffect, useState } from "react";
import { hotelsCollection } from "../lib/controller";
import { NewHotelType } from "../types/hotel";
import Information from "./Information";

function Hotel() {
  const [hotels, setHotels] = useState<NewHotelType[]>([]);
  const [search, setSearch] = useState("");
  const [sortState, setSortState] = useState("");
  const [isLoading, setIsLoading] = useState(false);

  useEffect(
    () =>
      onSnapshot(hotelsCollection, (snapshot: QuerySnapshot<DocumentData>) => {
        setHotels(
          snapshot.docs.map(doc => {
            return {
              id: doc.id,
              ...doc.data(),
            };
          })
        );
      }),
    []
  );

  if (isLoading) return <div>加载中..</div>;

  return (
    <div className="card">
      <select
        className="select"
        defaultValue=""
        onChange={(e) => setSortState(e.target.value)}
      >
        <option value="disabled">筛选</option>
        <option value="name">名称</option>
        <option value="price">价格</option>
      </select>
      <div className="search">
        <input
          className="inputsearch"
          value={search}
          type="text"
          placeholder="搜索酒店"
          onChange={(e) => setSearch(e.target.value)}
        />
      </div>
      {hotels && hotels.length ? (
        <div>
          {hotels
            ?.filter((item: NewHotelType) => {
              if (search === "" && !search.length) {
                return item;
              }
              return item?.title === search;
            })
            .sort((a, b) => {
              if (sortState === "name") {
                if (a.title && b.title) return a.title.localeCompare(b.title);
              }
              if (sortState === "price") {
                return Number(a.perNight) - Number(b.perNight);
              }
              return 0;
            })
            ?.map((hotel: NewHotelType) => (
              <Information key={hotel.id} hotel={hotel} />
            ))}
        </div>
      ) : !isLoading ? (
        <h2 className="no-hotels">暂无酒店请添加一个</h2>
      ) : <div>加载中..</div>}
    </div>
  );
}

export default Hotel;

希望这对您有帮助。如果您需要进一步的翻译或有其他问题,请随时告诉我。

英文:

I have simple react component where I render a list of hotels. I fetch data from firebase. I added a statement which I want to render ONLY when there aren't any hotels on list. Currently this h2 tag renders every time when I refresh the browser, or click on the go back button. Could you please kindly advise how can I make it to render only when needed?

import { DocumentData, onSnapshot, QuerySnapshot } from &quot;firebase/firestore&quot;;
import { useEffect, useState } from &quot;react&quot;;
import { hotelsCollection } from &quot;../lib/controller&quot;;
import { NewHotelType } from &quot;../types/hotel&quot;;
import Information from &quot;./Information&quot;;
function Hotel() {
const [hotels, setHotels] = useState&lt;NewHotelType[]&gt;([]);
const [search, setSearch] = useState(&quot;&quot;)
const [sortState, setSortState] = useState(&quot;&quot;);
const [isLoading, setIsLoading] = useState(false);
useEffect(
() =&gt;
onSnapshot(hotelsCollection, (snapshot: QuerySnapshot&lt;DocumentData&gt;) =&gt; {
setHotels(
snapshot.docs.map(doc =&gt; {
return {
id: doc.id,
...doc.data(),
};
})
);
}),
[]
);
if(isLoading) return &lt;div&gt;Loading..&lt;/div&gt;
return (
&lt;div className=&quot;card&quot;&gt;
&lt;select
className=&quot;select&quot;
defaultValue={&quot;&quot;}
onChange={(e) =&gt; setSortState(e.target.value)}
&gt;
&lt;option value=&quot;disabled&quot;&gt;Filter By&lt;/option&gt;
&lt;option value=&quot;name&quot;&gt;Name&lt;/option&gt;
&lt;option value=&quot;price&quot;&gt;Price&lt;/option&gt;
&lt;/select&gt;
&lt;div className=&quot;search&quot;&gt;
&lt;input
className=&quot;inputsearch&quot;
value={search}
type=&quot;text&quot;
placeholder=&quot;Search for the hotel&quot;
onChange={(e) =&gt; setSearch(e.target.value)}
/&gt;
&lt;/div&gt;
{hotels &amp;&amp; hotels.length ? (
&lt;div&gt;
{hotels
?.filter((item: NewHotelType) =&gt; {
if (search === &quot;&quot; &amp;&amp; !search.length) {
return item;
}
return item?.title === search;
})
.sort((a, b) =&gt; {
if (sortState === &quot;name&quot;) {
if (a.title &amp;&amp; b.title) return a.title.localeCompare(b.title);
}
if (sortState === &quot;price&quot;) {
return Number(a.perNight) - Number(b.perNight);
}
return 0;
})
?.map((hotel: NewHotelType) =&gt; (
&lt;Information key={hotel.id} hotel={hotel} /&gt;
))}
&lt;/div&gt;
) : !isLoading ? (
&lt;h2 className=&quot;no-hotels&quot;&gt;There are no hotels. Please add one&lt;/h2&gt;
) : &lt;div&gt;Loading..&lt;/div&gt;}
&lt;/div&gt;
);
}
export default Hotel;

答案1

得分: 1

你所做的是正确的。你需要管理加载状态。当你的组件首次渲染时,hotels状态是一个空数组,所以直到它被数据填充之前,你需要显示一个加载器,这样你就确保标题只在数组为空且数据加载完成后才渲染。

一种方法是设置一个初始为true的加载状态。一旦快照成功并将数据推送到状态中,你将把加载状态设置为false。换句话说,在setHotels(...)之后,你要把isLoading设置为false。

现在在你的JSX中,你可以为加载状态添加一个短路条件,例如:if(loading) return &lt;Loader /&gt;

更新:

function Hotel() {
  const [hotels, setHotels] = useState<NewHotelType[]>([]);
  const [search, setSearch] = useState("");
  const [sortState, setSortState] = useState("");
  const [isLoading, setIsLoading] = useState(true);

  useEffect(
    () =>
      onSnapshot(hotelsCollection, (snapshot: QuerySnapshot<DocumentData>) => {
        setHotels(
          snapshot.docs.map((doc) => {
            return {
              id: doc.id,
              ...doc.data(),
            };
          })
        );
        setIsLoading(false)
      }),
    []
  );

  if (isLoading) return <div>Loading..</div>;

  return (
    <div className="card">
      <select
        className="select"
        defaultValue=""
        onChange={(e) => setSortState(e.target.value)}
      >
        <option value="disabled">Filter By</option>
        <option value="name">Name</option>
        <option value="price">Price</option>
      </select>
      <div className="search">
        <input
          className="inputsearch"
          value={search}
          type="text"
          placeholder="Search for the hotel"
          onChange={(e) => setSearch(e.target.value)}
        />
      </div>
      {hotels && hotels.length ? (
        <div>
          {hotels
            ?.filter((item: NewHotelType) => {
              if (search === "" && !search.length) {
                return item;
              }
              return item?.title === search;
            })
            .sort((a, b) => {
              if (sortState === "name") {
                if (a.title && b.title) return a.title.localeCompare(b.title);
              }
              if (sortState === "price") {
                return Number(a.perNight) - Number(b.perNight);
              }
              return 0;
            })
            ?.map((hotel: NewHotelType) => (
              <Information key={hotel.id} hotel={hotel} />
            ))}
        </div>
      ) : (
        <h2 className="no-hotels">There are no hotels. Please add one</h2>
      )}
    </div>
  );
}
英文:

What you've done is correct. What you need, is to manage the loading state as well. When your component first renders your hotels state is an empty array, so till it gets filled by the data, you need to show a Loader, that way you make sure that the title is only rendering when the array is empty and the data has finished loading.

One way of doing it, is having a loading state that you set initially to true. and once your snapshot is successful and you pushed the data to the state, you will set the loading to false. In other words, after setHotels(...) you want to setLoading to false.

Now in your JSX you want to add a short circuit for the loading state,eg: if(loading) return &lt;Loader /&gt;

Update:

function Hotel() {
const [hotels, setHotels] = useState&lt;NewHotelType[]&gt;([]);
const [search, setSearch] = useState(&quot;&quot;);
const [sortState, setSortState] = useState(&quot;&quot;);
const [isLoading, setIsLoading] = useState(true);
useEffect(
() =&gt;
onSnapshot(hotelsCollection, (snapshot: QuerySnapshot&lt;DocumentData&gt;) =&gt; {
setHotels(
snapshot.docs.map((doc) =&gt; {
return {
id: doc.id,
...doc.data(),
};
})
);
setIsLoading(false)
}),
[]
);
if (isLoading) return &lt;div&gt;Loading..&lt;/div&gt;;
return (
&lt;div className=&quot;card&quot;&gt;
&lt;select
className=&quot;select&quot;
defaultValue={&quot;&quot;}
onChange={(e) =&gt; setSortState(e.target.value)}
&gt;
&lt;option value=&quot;disabled&quot;&gt;Filter By&lt;/option&gt;
&lt;option value=&quot;name&quot;&gt;Name&lt;/option&gt;
&lt;option value=&quot;price&quot;&gt;Price&lt;/option&gt;
&lt;/select&gt;
&lt;div className=&quot;search&quot;&gt;
&lt;input
className=&quot;inputsearch&quot;
value={search}
type=&quot;text&quot;
placeholder=&quot;Search for the hotel&quot;
onChange={(e) =&gt; setSearch(e.target.value)}
/&gt;
&lt;/div&gt;
{hotels &amp;&amp; hotels.length ? (
&lt;div&gt;
{hotels
?.filter((item: NewHotelType) =&gt; {
if (search === &quot;&quot; &amp;&amp; !search.length) {
return item;
}
return item?.title === search;
})
.sort((a, b) =&gt; {
if (sortState === &quot;name&quot;) {
if (a.title &amp;&amp; b.title) return a.title.localeCompare(b.title);
}
if (sortState === &quot;price&quot;) {
return Number(a.perNight) - Number(b.perNight);
}
return 0;
})
?.map((hotel: NewHotelType) =&gt; (
&lt;Information key={hotel.id} hotel={hotel} /&gt;
))}
&lt;/div&gt;
) : (
&lt;h2 className=&quot;no-hotels&quot;&gt;There are no hotels. Please add one&lt;/h2&gt;
)}
&lt;/div&gt;
);
}

huangapple
  • 本文由 发表于 2023年3月7日 00:46:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75653555.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定