【问题标题】:Effects with Cleanup - cancels pending API calls in cleanup清理效果 - 取消清理中待处理的 API 调用
【发布时间】:2022-01-14 05:44:27
【问题描述】:

React 相册应用: 任务是构建一个 React 应用程序来查看用户拥有的相册。它应该包含左侧的用户列表。在右侧,它应该有一个所选用户拥有的专辑列表。当您点击用户时,相册列表应该会发生变化。

错误:

        * cancels pending API calls in cleanup => Calling API with userId=undefined
    
        *  Displays a list albums when user is selected => Expected: "https://jsonplaceholder.typicode.com/albums?userId=2", ObjectContaining {"signal": Any<Object>},  Received
          3: "https://jsonplaceholder.typicode.com/albums?userId=undefined", {"signal": {}}
          4: "https://jsonplaceholder.typicode.com/users", {"signal": {}}

          Number of calls: 4

          Calling API with userId=undefined
// App.js
import React, { useState, useEffect } from "react";
import "./App.css";
import AlbumList from "./AlbumList";
import UserList from "./UserList";
function App() {
  const controller = new AbortController();
  const [users, setUsers] = useState([]);
  const [user, setCurrentUser] = useState({});
  const [albums, setAlbums] = useState([]);
  document.title = 'Awesome Album App';

  const userUrl = "https://jsonplaceholder.typicode.com/users";

  // Loading Albums
  useEffect(() => {
    const albumUrl = `https://jsonplaceholder.typicode.com/albums?userId=${user.id}`;
    async function loadAlbums() {
      try {
        const response = await fetch(albumUrl, { signal: controller.signal });
        const json = await response.json();
        console.log("Logging json: ", json)
        setAlbums(json)
      } catch (err) {
        if (err.name === 'AbortError') {
          console.log('Aborted', err)
        } else {
          throw err;
        }
      }
    }
    loadAlbums();
    console.log("After loadAlbums: ", albums)
    
    return () => {
      controller.abort();
      document.title = "";
    }
  },[user.id])
  
  // Loading Users
  useEffect(() => {
    async function loadUser() {
      try {
        const response = await fetch(userUrl, { signal: controller.signal });
        const data = await response.json();
        setUsers(...users, data);
      } catch (err) {
        if (err.name === 'AbortError') {
          console.log('Aborted', err)
          setUsers([])
        } else {
          throw err;
        }
      }
    }
    loadUser();
    return () => {
      controller.abort();
    }
  },[])

  return (
    <div className="App">
      <div className="left column">
      <UserList users={users} setCurrentUser={setCurrentUser} />
      </div>
      <div className="right column">
        <AlbumList user={user} albums={albums} />
      </div>
    </div>
  );
}

export default App;

// AlbumList.js

import React from "react";

function AlbumList({ user = {}, albums }) {
  console.log("albumList", albums)
  
  if(albums.length&& user){   
    
    return  albums.map((album, index)=>{
    return <p key={index}>{album.id} - {album.title}</p>
    });  
  }

  return  <p>Please click on a user name to the left</p>

}

export default AlbumList;


// UserList.js

import React from "react";

function UserList({ users, setCurrentUser }) {

  return (
    <ul className="user-list">
      {users.map((user) => (
        <p key={user.id}>
          <button type="button" onClick={() => setCurrentUser(user)}>
            {user.name}
          </button>
        </p>
      ))}
    </ul>
  );
}

export default UserList;

具体说明 从https://jsonplaceholder.typicode.com/users 加载用户。 从https://jsonplaceholder.typicode.com/albums?userId=${user.id} 加载相册。 创建其他组件,然后由 App 组件使用。 使用内联样式和/或 CSS 文件对组件进行样式设置。它不需要与示例图像的外观相匹配,但应具有合理的布局。 文档标题应为“Awesome Album App”,并在清理组件时重置为原始文档标题。您可以通过访问 document.title 并存储该值来获取原始标题。 当用户更改时,应取消待处理的 API 调用。 应尽可能“提升”状态。 不要对用户数量或用户拥有的专辑数量做任何假设。 如果未选择用户,右侧应显示“请单击左侧的用户名” 专辑列表应包括专辑ID和专辑标题

【问题讨论】:

  • 我看到你的用户对象是空的。所以 user.id 必须是未定义的。因此,您无法成功拨打电话。或者是否有一段代码实际上设置了用户信息? jsonplaceholder.typicode.com/albums?userId=5 我在浏览器中运行了它,它检索了一些数据。所以这意味着 user.id 变量是代码中的问题部分
  • 您的代码看起来不错,但我无法看到您获取用户信息的任何地方
  • 我从 userUrl = "jsonplaceholder.typicode.com/users" 获取用户信息。

标签: javascript reactjs react-hooks use-effect


【解决方案1】:

我是在代码沙盒上做的 https://codesandbox.io/s/polished-browser-w8c9p?file=/src/App.js:0-1142 它会呈现一个列表

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

function App() {
  const controller = new AbortController();
  const [users, setUsers] = useState([]);
  document.title = "Awesome Album App";
  console.log(users, "1");

  const userUrl = "https://jsonplaceholder.typicode.com/users";

  // Loading Users
  useEffect(() => {
    async function loadUser() {
      try {
        const response = await fetch(userUrl, { signal: controller.signal });
        const data = await response.json();
        console.log(data);
        setUsers([...users, ...data]);
        console.log(users);
      } catch (err) {
        if (err.name === "AbortError") {
          console.log("Aborted", err);
          setUsers([]);
        } else {
          throw err;
        }
      }
    }

    loadUser();
  }, []);
  console.log(users, "body");
  return (
    <div className="App">
      {users.map((user) => {
        return (
          <ul>
            <li>{user.name}</li>
            <li>{user.username}</li>
            <li>{user.id}</li>
            <li>{user.phone}</li>
          </ul>
        );
      })}
    </div>
  );
}

export default App;

它不能解决所有问题,只是为了查看一个工作组件。我认为您收到错误的原因是因为当您进行 api 调用时,尽管我们没有用户详细信息。 userId undefined ergo,无法正确拨打电话。

我建议你将 loadusers 函数移到 loadalbum 函数之上。因为在我们运行代码时顺序很重要。

【讨论】:

  • 我将 loadUser 函数移到了 loadAlbum 函数之上,我得到了同样的错误:cancels pending API calls in cleanup => Calling API with userId=undefined
  • 今晚我会尝试看看我们能做些什么
【解决方案2】:
//App.js
import React, { useState, useEffect } from "react";
import "./App.css";
import AlbumList from "./AlbumList";
import UserList from "./UserList";

function App() {
  const controller = new AbortController();
  const [users, setUsers] = useState([]);
  const [user, setCurrentUser] = useState({});
  const [albums, setAlbums] = useState([]);
  document.title = 'Awesome Album App';

  const usersUrl = "https://jsonplaceholder.typicode.com/users";

  // Loading Users
  useEffect(() => {
    async function loadUsers() {
      try {
        const response = await fetch(usersUrl, { signal: controller.signal });
        const data = await response.json();
        setUsers(...users, data);
      } 
      catch (err) {
        if (err.name === 'AbortError') {
          console.log('Aborted', err)
          setUsers([])
        } else {
          throw err;
        }
      }
    }
    loadUsers();

    return () => {
      controller.abort();
    }
  },[]);

  // Loading Albums
  useEffect(() => {
    const albumUrl = `https://jsonplaceholder.typicode.com/albums?userId=${user.id}`;
    async function loadAlbums() {

      if(user.id){
        try {
          const response = await fetch(albumUrl, { signal: controller.signal });
          const json = await response.json();
          console.log("Logging json: ", json)
          setAlbums(json)
        } 
        catch (err) {
          if (err.name === 'AbortError') {
            console.log('Aborted', err)
          } else {
            throw err;
          }
        }
      }
    }

    loadAlbums();
    console.log("After loadAlbums: ", albums)
    
    return () => {
      setAlbums([])
      controller.abort();
      document.title = "";
    }
  },[user]);

  return (
    <div className="App">
      <div className="left column">
        <UserList users={users} setCurrentUser={setCurrentUser} />
      </div>
      <div className="right column">
        {(albums.length&&user)? <h3>{user.name} Albums</h3> : <h3></h3>}
        <AlbumList user={user} albums={albums} />
      </div>
    </div>
  );
}

export default App;
// AlbumList.js

import React from "react";

function AlbumList({ user = {}, albums }) {
  console.log("albumList", albums)
  
  if(albums.length&& user){   
    
    return  albums.map((album, index)=>{
    return <p key={index}>{album.id} - {album.title}</p>
    });  
  }

  return  <p>Please click on a user name to the left</p>

}

export default AlbumList;

// UserList.js

import React from "react";

function UserList({ users, setCurrentUser }) {

  return (
    <ul className="user-list">
      {users.map((user) => (
        <p key={user.id}>
          <button type="button" onClick={() => setCurrentUser(user)}>
            {user.name}
          </button>
        </p>
      ))}
    </ul>
  );
}

export default UserList;

【讨论】:

  • 这是正确的代码。谢谢你帮助我。
猜你喜欢
  • 1970-01-01
  • 2022-11-24
  • 2012-08-03
  • 1970-01-01
  • 2015-08-27
  • 2018-01-05
  • 2020-10-28
  • 2013-03-02
  • 1970-01-01
相关资源
最近更新 更多