【问题标题】:TypeError: data.map is not a function, even though the data is an array (i think)TypeError: data.map 不是一个函数,即使数据是一个数组(我认为)
【发布时间】:2021-09-18 10:38:48
【问题描述】:

我正在使用 Django rest 发送数据以做出反应:

[
{
    "id": 1,
    "username": "Dewalian",
    "question": [
        {
            "id": 5,
            "title": "how to cook?"
        },
        {
            "id": 6,
            "title": "how to exercise?"
        }
    ]
},
{
    "id": 2,
    "username": "Edward",
    "question": []
},
{
    "id": 3,
    "username": "Jeremy",
    "question": []
}
]

然后使用视频教程中的自定义钩子获取它:

import useFetch from "./UseFetch";
import DataList from "./DataList";

const MainContent = () => {
    const {data: question, isPending, error} = useFetch("http://127.0.0.1:8000/api/user/");

return ( 
    <div className="main-content">
        {error && <div>{error}</div>}
        {isPending && <div>Loading...</div>}
        {question && <DataList data={question} />}
    </div>
      );
 };

export default MainContent;

最后我把它映射到另一个组件上:

const DataList = (data) => {
return ( 
      <div>
         {data.map(user => (
            <div key={user.id}>
                <p>{user.username}</p>
            </div>
         ))}
      </div>
      );
 }

 export default DataList;

但我得到“TypeError:data.map 不是函数”。我很确定问题出在 json 上,但我不知道出了什么问题。那么,出了什么问题以及如何解决这个问题?谢谢。

编辑

这是 useFetch 自定义钩子(只是带有 useEffect 的普通获取):

import { useEffect, useState } from "react";

const useFetch = (url) => {
    const [data, setData] = useState(null);
    const [isPending, setIsPending] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const abortCont = new AbortController();

        fetch(url)
        .then(res => {
            if (!res.ok) {
                throw Error ("Couldn't fetch data from that resource.")
            };
            return res.json()
        })
        .then(data => {
            setData(data);
            setIsPending(false);
            setError(null);
        })
        .catch(err => {
            if (err.name === "AbortError") {
                console.log("fetch aborted.")
            } else {
                setIsPending(false);
                setError(err.message);
            };
        });

        return () => abortCont.abort();
    }, [url]);

    return {data, isPending, error};
};

export default useFetch;

和 api/用户:

@api_view(["GET"])
def user_all(request):
    model_data = User.objects.all()
    serializer = UserSerializer(model_data, many=True)
    return Response(serializer.data)

【问题讨论】:

  • 我认为如果你通过在 useFetch 行设置断点来查看返回值是什么来调试它会很容易。
  • 返回值和上面的json数据一模一样(另外两个基本是检查“加载”和错误值)

标签: json reactjs django django-rest-framework


【解决方案1】:

不要忘记将 {} 放在参数上:

const DataList = ({data}) => {
return ( 
      <div>
         {data.map(user => (
            <div key={user.id}>
                <p>{user.username}</p>
            </div>
         ))}
      </div>
      );
 }

 export default DataList;

【讨论】:

    【解决方案2】:

    对不起,我不能评论这个帖子,我稍后会编辑这个。

    您介意分享您的自定义钩子的代码吗? 如果您共享 /api/user/ 视图代码,那就更好了。

    const data = useFetch("http://127.0.0.1:8000/api/user/");
    console.log(data)
    

    您将在 google 开发者工具 -> 控制台中查看它。 您还应该能够直接从网络查看请求 -> 单击自定义 url 中的响应 -> 在选项中查找响应。

    您还可以在后端使用 print() 查看响应,

    或者您可以使用 httpie 或 postman 使用有效令牌直接向您的 API 发送请求(

    • 如果您的 api 使用 isAuthenticated 权限,那么您需要从后端复制用户令牌

    • 如果您的 api 使用 AllowAny,那么您不需要来自后端的访问令牌,您可以直接从任何请求中获取数据。

    • 如果您的 api 使用 isAdmin,那么您需要管理员访问令牌。

    • 等等。取决于您的许可。

    [编辑] 看起来像您的自定义钩子中的问题,在 fetch 语句中, 它正在返回 res.json() 导入 { useEffect, useState } from "react";

    const useFetch = (url) => {
        const [data, setData] = useState(null);
        const [isPending, setIsPending] = useState(true);
        const [error, setError] = useState(null);
    
        useEffect(() => {
            const abortCont = new AbortController();
    fetch(url, {
      method: 'GET', // or 'PUT'
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    })
    .then(response => response.json())
    .then(data => {
      setData(data);
     setIsPending(false);
      setError(null);
    })
    .catch((error) => {
      if (err.name === "AbortError") {
                    console.log("fetch aborted.")
                } else {
                    setIsPending(false);
                    setError(err.message);
                };
    });
    
            return () => abortCont.abort();
        }, [url]);
    
        return {data, isPending, error};
    };
    
    export default useFetch;
    
    

    然后你可以像这样解构你获取的数据

    const { data, isPending, error } = useFetch(url);
    

    现在 data 是一个数组,并且有 data.questions、data.username

    在您的地图功能中,您可以像这样使用它

    {data.map(i => {
    // {i.id}
    // {i.question}
    // {i.username} 
    })
    }
    
    

    【讨论】:

    • 已编辑,我很确定这与自定义挂钩无关。我真的不知道你在说什么令牌,它像 django 上的 csrf 令牌吗?
    • 你可能是对的,你的问题一定是在解构响应你介意分享响应。这不是 CSRF 令牌,这是另一回事,在 REST API 中有访问令牌,可防止未经授权的用户从您的 API 获取数据,您可以使用 permission_classes 属性自定义权限访问,通常它是一个元组或列表,在其中添加要了解更多权限类,请访问此链接:django-rest-framework.org/api-guide/authentication
    • 这是response
    • 我编辑了我的答案,请检查并告诉我。
    • 非常感谢您的帮助,但结果我忘了把 { } 放在 DataList 组件的参数上 :')
    猜你喜欢
    • 2021-07-18
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-12
    • 2018-04-28
    • 1970-01-01
    相关资源
    最近更新 更多