我使用 React 错了吗?

发布于 2025-01-13 03:34:55 字数 3563 浏览 0 评论 0原文

反应:我做错了吗?

我使用 React 已经有一段时间了,我已经能够利用 React 提供的功能创建一些非常酷的项目;钩子、道具等等。事情是这样的。我的工作流程总是会停止,当我尝试在本地函数和全局函数之间传递变量和状态时,我最终会遇到意大利面条式代码的糟糕情况。 9/10 我最终陷入困境并违反了 React Hooks 规则,并通过一种非常普通的 JS 做事方式摆脱了困境。然后我对自己说:“这真是太棒了……不,我的意思是:如果我在尝试做一些比在页面上渲染组件更高级的事情时最终编写了普通 JS,为什么还要使用 React?”。难道我的做法全错了?

下面是一个示例:我有一个网页,它获取用 Express 编写的 API,然后从 MongoDB 数据库返回数据。我使用自定义挂钩通过异步函数进行获取,然后在页面上显示所有内容。我有一个可以渲染所有内容的功能组件。我还使用 API fetch 发送一些查询数据,在本例中是数字的字符串表示形式,这反过来又设置了从数据库收集的元素数量的限制。在 useEffect 挂钩(位于我之前提到的自定义挂钩内)上,我有要显示为依赖项的元素数量,以便每次该值发生变化时我都会获取 API。该值又由 1-1000 之间的滑块选择。每次我获取时,组件都会再次渲染并且所有内容都会闪烁。这是因为来自数据库的数据以及我的 h1、滑块和 p 标签都在同一个组件中。我想避免这种情况,所以我最初的想法是将除数据库中的数据之外的所有内容提取到不同的组件并单独渲染。这就是问题所在。设置状态的滑块值,自定义挂钩又使用状态作为查询参数发送到 API,它们之间不再有任何连接。我使用 React 的方式是错的吗?这是上下文 API 适合使用的地方吗? 我基本上想在不同的功能组件之间共享状态,并将它们单独呈现在网页上。

这是我的前端代码:

import React, { useEffect, useState } from "react";

function useLoading(loadingFunction, sliderValue) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState();
  const [data, setData] = useState([]);

  async function load() {
    try {
      setLoading(true);
      setData(await loadingFunction());
    } catch (e) {
      setError(e);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    load();
  }, [sliderValue]);

  return { loading, error, data };
}

async function fetchJSON(url, sliderValue) {
  const res = await fetch(url + `?numberOfMovies=${sliderValue}`);
  if (!res.ok) {
    throw new Error(`${res.status}: ${res.statusText}`);
  }
  return await res.json();
}

function randomNumber() {
  return Math.floor(Math.random() * 20000000000);
}

function LoadingPage() {
  return (
    <>
      <div className="loading one" />
      <div className="loading two" />
      <div className="loading three" />
      <div className="loading four" />
    </>
  );
}

function MovieCard({ movie: { title, plot, year, poster } }) {
  return (
    <div className={"movie-card"}>
      <h3>
        {title} ({year})
      </h3>
      <p>{plot}</p>
      {poster && <img width={100} src={poster} alt="Poster" />}
    </div>
  );
}

function ListMovies() {
  const [sliderValue, setSliderValue] = useState("300");
  const { loading, error, data } = useLoading(
    async () => fetchJSON("/api/movies", sliderValue),
    sliderValue
  );

  if (loading) {
    return <LoadingPage />;
  }
  if (error) {
    return (
      <div>
        <h1>Error</h1>
        <div>{error.toString()}</div>
      </div>
    );
  }

  function handleSliderChange(e) {
    let value = (document.getElementById("slider").value = e.target.value);
    document.getElementById("slider-value").innerHTML =
      value <= 1 ? `${value} movie` : `${value} movies`;
    setSliderValue(value);
  }

  return (
    <div className={"movies-container"}>
      <h1>Movies</h1>
      <p>Sorted by highest rated on Metacritic. All movies are from Ukraine.</p>
      <input
        onChange={handleSliderChange}
        type="range"
        min="1"
        max="1000"
        className="slider"
        id="slider"
      />
      <p id="slider-value" />
      <div>
        {data.map((movie) => (
          <MovieCard key={randomNumber()} movie={movie} />
        ))}
      </div>
    </div>
  );
}

export function MainPage() {
  return (
    <div>
      <ListMovies />
    </div>
  );
}

React: am I doing it wrong?

So I’ve been working with React for a while, and I’ve been able to create some really cool projects by utilizing what React has to offer; Hooks, props, etc. The thing is. My workflow always comes to a stop and I end up having a bad case of spaghetti-code when I try to pass variables and state between local and global functions. 9/10 I end up getting stuck and disobeying the React Hooks Rules, and have hack my way out of it with a very vanilla JS way of doing things. And then I think to myself: “What a wonderf… No, I mean: Why am I using React if I end up writing vanilla JS when I try to do something that is a bit more advanced than rendering components on a page?”. Is my approach all wrong?

Here's an example: I have a webpage which fetches to an API written in Express, which in turn returns data from a MongoDB database. I use a custom hook to fetch with an async function, and then I display everything on a page. I have a functional component that renders out everything. I also send some query-data with the API fetch, which in this example is a string representation of numbers, which in turn sets the limit of how many elements are gathered from the database. And on the useEffect hook – which is inside the custom hook I mentioned earlier – I have the number of elements to display as a dependency, so that I fetch the API every time that value changes. That value in turn, is chosen by a slider between 1-1000. Every time I fetch, the component renders again and everything flashes. This is because the data from the DB, as well as my h1, slider, and p-tags, are all in the same component. I want to avoid that, so my initial thought is to extract everything BUT the data from the DB, to a different component and render it separately. And this is where it goes wrong. The slidervalue which sets state, which in turn the custom hook uses to send as a query parameter to the API, they do not have any connection to each other anymore. Am I using React all wrong? Is this where the context API would be smart to use?
I basically want to share state between to different functional components, and render them separately on a webpage.

This is my frontend code:

import React, { useEffect, useState } from "react";

function useLoading(loadingFunction, sliderValue) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState();
  const [data, setData] = useState([]);

  async function load() {
    try {
      setLoading(true);
      setData(await loadingFunction());
    } catch (e) {
      setError(e);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    load();
  }, [sliderValue]);

  return { loading, error, data };
}

async function fetchJSON(url, sliderValue) {
  const res = await fetch(url + `?numberOfMovies=${sliderValue}`);
  if (!res.ok) {
    throw new Error(`${res.status}: ${res.statusText}`);
  }
  return await res.json();
}

function randomNumber() {
  return Math.floor(Math.random() * 20000000000);
}

function LoadingPage() {
  return (
    <>
      <div className="loading one" />
      <div className="loading two" />
      <div className="loading three" />
      <div className="loading four" />
    </>
  );
}

function MovieCard({ movie: { title, plot, year, poster } }) {
  return (
    <div className={"movie-card"}>
      <h3>
        {title} ({year})
      </h3>
      <p>{plot}</p>
      {poster && <img width={100} src={poster} alt="Poster" />}
    </div>
  );
}

function ListMovies() {
  const [sliderValue, setSliderValue] = useState("300");
  const { loading, error, data } = useLoading(
    async () => fetchJSON("/api/movies", sliderValue),
    sliderValue
  );

  if (loading) {
    return <LoadingPage />;
  }
  if (error) {
    return (
      <div>
        <h1>Error</h1>
        <div>{error.toString()}</div>
      </div>
    );
  }

  function handleSliderChange(e) {
    let value = (document.getElementById("slider").value = e.target.value);
    document.getElementById("slider-value").innerHTML =
      value <= 1 ? `${value} movie` : `${value} movies`;
    setSliderValue(value);
  }

  return (
    <div className={"movies-container"}>
      <h1>Movies</h1>
      <p>Sorted by highest rated on Metacritic. All movies are from Ukraine.</p>
      <input
        onChange={handleSliderChange}
        type="range"
        min="1"
        max="1000"
        className="slider"
        id="slider"
      />
      <p id="slider-value" />
      <div>
        {data.map((movie) => (
          <MovieCard key={randomNumber()} movie={movie} />
        ))}
      </div>
    </div>
  );
}

export function MainPage() {
  return (
    <div>
      <ListMovies />
    </div>
  );
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

歌枕肩 2025-01-20 03:34:55

这可能足以将国家“提升”到共同的祖先。 React 中的状态管理是一个令人惊讶的复杂主题,值得阅读标准方法。提升状态就是其中之一,因为组件“通常”不会“水平”地相互通信。道具流下来。还有其他方法可以管理它,例如 Context 或 Redux,甚至是“非”React 方法,例如 pub/sub。

好消息是,在亲身体验过这些痛点后,您会欣赏一些解决问题的模式。

在我看来,我不确定是否存在“错误”的做事方式,只要它有效即可。但肯定有一些方法会让生活变得艰难,也有一些方法会让生活变得更轻松。

如果您可以将问题简化为一个非常具体的问题,而无需太多解释,您可能会得到更好的帮助。

It might be enough to "lift" the state to a common ancestor. State management in React is a surprisingly complex topic and worth reading up on standard approaches. Lifting state is one of them, because components don't "usually" talk to each other "horizontally". Props flow down. There are other ways to manage this such as Context or Redux, or even "non" React approaches such as pub/sub.

The good news is that having experienced the pain points first hand, you'll appreciate some of the patterns for solving the problems.

In my opinion I'm not sure there is a "wrong" way to do things, as long as it works. But there are definitely approaches that make life hard and others that make life easier.

If you could whittle down your issue to a very specific question, without so much explanation, you're likely to get better help.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文