簡體   English   中英

React JS 設置加載器 UI 的正確方法

[英]React JS proper way to set a loader UI

我試圖在 Code Sandbox 中復制我的本地項目,但我發現了我的錯誤。 我在這些項目中試圖做的是在從 Express + GraphQL 服務器獲取一些數據時顯示一個加載器微調器,但相反,加載器不會在加載數據時隱藏。

這是我獲取數據的代碼:

import React, { useEffect, useContext, useState } from "react";
import GlobalContext from "../../context/Global/context";
import gql from "graphql-tag";
import { useLazyQuery } from "@apollo/react-hooks";

const GET_USERS = gql`
  query {
    users {
      id
      name
      address {
        street
      }
    }
  }
`;

export default props => {
  const globalContext = useContext(GlobalContext);
  const [getUsers, { called, loading, data: users }] = useLazyQuery(GET_USERS);

  useEffect(() => {
    getUsers();
  }, []);

  useEffect(() => {
    console.log(globalContext.loading)
    if (globalContext.loading && users.length) {
      globalContext.setLoading(false);
    }
  }, [globalContext, users]);

  if (called && loading) {
    globalContext.setLoading(true);
  }

  if (!users) {
    return <p>There are no users</p>;
  }
  console.log(users)

  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Street</th>
        </tr>
      </thead>
      <tbody>
        {users.map(user => {
          return (
            <tr>
              <td>{user.id}</td>
              <td>{user.name}</td>
              <td>{user.address.street}</td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
};

這是我設置加載器的代碼:

import React, { useContext, useEffect } from "react";
import { ApolloClient } from "apollo-boost";
import { ApolloProvider } from "@apollo/react-hooks";
import { HttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import Users from "./components/Users";
import GlobalContext from "./context/Global/context";
import Loader from "react-loader-spinner";

const cache = new InMemoryCache();
const link = new HttpLink({
  uri: "https://73rcp.sse.codesandbox.io/"
});

const client = new ApolloClient({
  link,
  cache
});

export default () => {
  const globalContext = useContext(GlobalContext);

  if (globalContext.loading) {
    return (
      <div
        style={{
          width: "100vw",
          height: "100vh",
          display: "flex",
          justifyContent: "center",
          alignItems: "center"
        }}
      >
        <Loader type="Puff" />
      </div>
    );
  }

  return (
    <ApolloProvider client={client}>
      <Users />
    </ApolloProvider>
  );
};

這是前端代碼

這是后端代碼

我總是遇到同樣的問題,不確定我做錯了什么,但我想這是因為當我設置globalContext.setLoading(true) ,組件會重新渲染,並且App.js的第一個組件比用戶組件加載。

如果這是否是錯誤,那么在從任何地方獲取任何數據時設置加載器微調器的正確方法是什么? 提前致謝。

所以經過一些調試,主要問題是Users組件在從服務器接收數據之前卸載。 另外,我遇​​到了一些命名沖突。

// App.js
export default () => {
  const globalContext = useContext(GlobalContext);
  // This is making the Users component unmount which won't allow to change the
  // global loading state once the data is received, hence the perpetual loading.
  if (globalContext.loading) {
    return (
      <div
        style={{
          width: "100vw",
          height: "100vh",
          display: "flex",
          justifyContent: "center",
          alignItems: "center"
        }}
      >
        <Loader type="Puff" />
      </div>
    );
  }

  return (
    <ApolloProvider client={client}>
      <Users />
    </ApolloProvider>
  );
};

一個簡單的解決方法是同時渲染Loader和 Users 組件,但是,前者將被絕對定位,因此用戶無法與Users組件交互。

export default () => {
  const globalContext = useContext(GlobalContext);

  return (
    <ApolloProvider client={client}>
      <Users />
      {globalContext.loading && (
        <div
          style={{
            width: "100vw",
            height: "100vh",
            display: "flex",
            position: "absolute",
            top: 0,
            backgroundColor: "white",
            justifyContent: "center",
            alignItems: "center"
          }}
        >
          <Loader type="Puff" />
        </div> 
      )}
    </ApolloProvider>
  );
};

Users組件上有一些問題:

// Users component
export default props => {
  const globalContext = useContext(GlobalContext);
  // data: users will not return users object, instead it is only renaming it. 
  // if we were to destructure it like data: { users }, an error would be thrown since 
  // data.users is undefined. 
  const [getUsers, { called, loading, data: users }] = useLazyQuery(GET_USERS);

  useEffect(() => {
    getUsers();
  }, [getUsers]);

  useEffect(() => {
    // data does not contain a length property since it is an object. Again an 
    // error would be thrown. 
    if (globalContext.loading && users.length) {
      globalContext.setLoading(false);
    }
  }, [globalContext, users]);

  if (called && loading) {
    globalContext.setLoading(true);
  }

  if (!users) {
    return <p>There are no users</p>;
  }

  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Street</th>
        </tr>
      </thead>
      <tbody>
        {/* Since data is not an array, map would be undefined (error again)*/}
        {users.map(user => {
          return (
            <tr>
              <td>{user.id}</td>
              <td>{user.name}</td>
              <td>{user.address.street}</td>
            </tr>
          );
        })}
      </tbody>
    </table>
  );
};

所以為了解決這個問題,我們有:

export default props => {
  const globalContext = useContext(GlobalContext);
  const [getUsers, { called, loading, data }] = useLazyQuery(GET_USERS);

  useEffect(() => {
    getUsers();
  }, [getUsers]);

  useEffect(() => {
    if (globalContext.loading && data) {
      globalContext.setLoading(false);
    }
  }, [globalContext, data]);

  if (called && loading) {
    globalContext.setLoading(true);
  }

  if (!data) {
    return <p>There are no users</p>;
  }

  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Name</th>
          <th>Street</th>
        </tr>
      </thead>
      <tbody>
        {data &&
          data.users.map(user => {
            return (
              <tr>
                <td>{user.id}</td>
                <td>{user.name}</td>
                <td>{user.address.street}</td>
              </tr>
            );
          })}
      </tbody>
    </table>
  );
};

所有更改都在此沙箱中

在從服務器獲取數據或向服務器發送數據時,有多種方法可以在 React 應用程序中實現加載器。

最好的方法是創建一個高階組件來加載包裝特征組件或基於布爾標志的加載器。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM