繁体   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