【问题标题】:React only sometimes is throwing cannot map property 'map' of undefined反应只是有时会抛出无法映射未定义的属性'map'
【发布时间】:2021-04-21 21:00:06
【问题描述】:

我是新来的反应和做一个项目。我正在构建一个包含 3 个卡片组件的页面。他们都使用反应查询和 Axios 来获取数据。我的页面有时可以工作,但总是随机向我抛出有关未定义属性“地图”的错误。我在 useState 挂钩中使用空数组作为初始状态,但有时我仍然会收到此错误。无法弄清楚为什么会这样。

这是我的卡片组件的代码。我还有两张代码相似的卡片。

import React,{useState} from "react";
import {Card, Spin, Divider, List, Tag} from 'antd';
import axiosInstance from "../axios";
import {  useQuery } from 'react-query';

const { Meta } = Card;



const MyPlantMatrix=()=> {
const[myPlantMatrix,setMyPlantMatrix] = useState([]);
const {status, data, error, isFetching} = useQuery('myplantmatrix', async ()=>{
    const {data} = await axiosInstance.get("api_url/?format=json");
    console.log(data);
    setMyPlantMatrix(data);
    return data;
})
    const renderedPlantMatrix = myPlantMatrix.map(
        plants => {
            return <Card style = {{ width:400}} key={plants}>
                <Meta title="My Assigned Plants"></Meta>
                <Divider/>
                 {plants.assigned_plant_code.map(
                     plantcode => {
                         return <Tag>{plantcode}</Tag>
                     }
                 ) }

            </Card>
        }
    )

    return(
        <div>
            {status === 'loading' ? (
                <Spin/>
            ): status === 'error' ? (
                error.message
            ):(
                <>
                    {renderedPlantMatrix}
                    <div>{
                        isFetching ? 'Data is updating in background...' : ''
                    }</div>
                </>
            )}
            
        </div>

    );
}

export default MyPlantMatrix;

这是我调用上述组件进行渲染的页面的代码。

import React, {useState,useEffect} from "react";
import { List,Card } from 'antd';

import MyBuyerProfile from "../../components/MyBuyerProfile";
import MyAssignedPlants from "../../components/MyPlantMatrix";
import MyMarketMatrix from "../../components/MyMarketMatrix";
const gridStyle = {
    width: '25%',
    textAlign: 'center',
};

function MyAccountView() {

        return (
            <Card title="My Profile">
                <List
                    grid={{
                        gutter: 16,
                        xs: 1,
                        sm: 2,
                        md: 4,
                        lg: 4,
                        xl: 6,
                        xxl: 3,
                    }}>
                    <List.Item>
                        <MyBuyerProfile/>
                    </List.Item>
                    <List.Item>
                        <MyAssignedPlants/>
                    </List.Item>
                    <List.Item>
                        <MyMarketMatrix/>
                    </List.Item>
                </List>
            </Card>
        );

}

export default MyAccountView;

这是我添加 Auth 标头的 Axios 实例的代码。我不断收到警报错误 “发生服务器/网络错误看起来 CORS 可能是问题所在。对此很抱歉 - 我们会尽快修复它。”有时当我刷新。我无法弄清楚为什么会触发它。

// import React from "react";
import axios from 'axios';
// import { Alert } from 'antd';
const baseURL = MyBaseURL;

const axiosInstance = axios.create({
    baseURL: baseURL,
    timeout: 5000,
    headers: {
        Authorization: localStorage.getItem('access_token')
            ? 'Bearer ' + localStorage.getItem('access_token')
            : null,
        'Content-Type': 'application/json',
        accept: 'application/json',
    },
});

axiosInstance.interceptors.response.use(
    (response) => {
        return response;
    },
    async function (error) {
        const originalRequest = error.config;

        if (typeof error.response === 'undefined') {
            alert(
                'A server/network error occurred. ' +
                'Looks like CORS might be the problem. ' +
                'Sorry about this - we will get it fixed shortly.'
            );

            return Promise.reject(error);
        }

        if (
            error.response.status === 401 &&
            originalRequest.url === baseURL + 'auth/jwt/refresh/'
        ) {
            window.location.href = '/account/Login/';
            return Promise.reject(error);
        }

        if (
            error.response.data.code === 'token_not_valid' &&
            error.response.status === 401 &&
            error.response.statusText === 'Unauthorized'
        ) {
            const refreshToken = localStorage.getItem('refresh_token');

            if (refreshToken) {
                const tokenParts = JSON.parse(atob(refreshToken.split('.')[1]));

                // exp date in token is expressed in seconds, while now() returns milliseconds:
                const now = Math.ceil(Date.now() / 1000);
                console.log(tokenParts.exp);

                if (tokenParts.exp > now) {
                    return axiosInstance
                        .post('/auth/jwt/refresh/', { refresh: refreshToken })
                        .then((response) => {
                            localStorage.setItem('access_token', response.data.access);
                            localStorage.setItem('refresh_token', response.data.refresh);

                            axiosInstance.defaults.headers['Authorization'] =
                                'Bearer ' + response.data.access;
                            originalRequest.headers['Authorization'] =
                                'Bearer ' + response.data.access;

                            return axiosInstance(originalRequest);
                        })
                        .catch((err) => {
                            console.log(err);
                        });
                } else {
                    console.log('Refresh token is expired', tokenParts.exp, now);
                    window.location.href = '/account/Login/';
                }
            } else {
                console.log('Refresh token not available.');
                window.location.href = '/account/Login/';
            }
        }

        // specific error handling done elsewhere
        return Promise.reject(error);
    }
);

export default axiosInstance;

请帮助我理解并指向相关资源以了解更多信息。

【问题讨论】:

  • 您只在两个地方使用了map。当您调用它们的东西是undefined 时,在它们上放置条件断点。看看你从 axios 得到的 data 是什么。它有时不是一个数组,或者有时是一个数组,其中至少一个条目不是对象,或者是一个没有 assigned_plant_code 属性的对象(或至少一个具有值 undefined 的对象)。
  • 这是 setMyPlantMatrix(data);总是设置一个数组?

标签: javascript reactjs axios react-query


【解决方案1】:

我认为 react-query 的工作原理存在概念上的误解:从 query-function 返回的数据将在 react-query 返回的 data 属性中提供给您。不需要任何进一步的本地状态 - 尤其是当您尝试在查询功能期间设置它时。如果您处于加载或错误状态,该数据可以是undefined,因此您需要先检查。还请查看文档中的所有examples

这是通常的样子,因为您的查询函数将返回一个数组:

const MyPlantMatrix=()=> {
  const {status, data, error, isFetching} = useQuery('myplantmatrix', async ()=>{
    const {data} = await axiosInstance.get("api_url/?format=json");
    console.log(data);
    return data;
  })

  // here, data will be potentially undefined
  
  if (status === 'loading') return <Spin/>
  if (status === 'error') return error.message

  // now, data should not be undefined

  const renderedPlantMatrix = data.map(...)

  return (
    <>
      {renderedPlantMatrix}
      <div>{isFetching ? 'Data is updating in background...' : ''}</div>
    </>
  )
}

或者,您可以在开始时进行if (data) 检查,然后呈现您的数据。如果您在重新获取时遇到错误,这可能会更好,因为那时您将遇到data 以及一个错误,因此您必须逐个决定 -如果最好显示重新获取错误或缓存具有的(陈旧)数据,或两者兼而有之,则以案例为基础。

【讨论】:

  • 谢谢,我已经实现了这个方法,但是,我现在意识到警报的问题是因为竞争条件。我在 MyAccount 页面中呈现 3 个组件。每个组件都有一个使用 react-query 实现的 axiosInstance.get 调用。
  • 如果所有组件同时挂载,react-query 将为您删除请求。如果它们连续挂载,请考虑设置一个小的staleTime
【解决方案2】:

我认为问题就在这里。您需要首先检查assigned_plant_code 是一个数组并且它有length &gt; 0? 将在检查长度之前确保 assigned_plant_code 存在

{plants.assigned_plant_code?.length && plants.assigned_plant_code.map(
                     plantcode => {
                         return <Tag>{plantcode}</Tag>
                     }
                 ) }

【讨论】:

    【解决方案3】:

    myPlantMatrix 应该是map 工作的数组,在数据获取之前和期间myPlantMatrix 将不是数组类型。检查是否数组然后map

    使用 Array.isArray(myPlantMatrix) &amp;&amp; myPlantMatrix.map(...

    【讨论】:

      【解决方案4】:

      我认为您不应该使用 useState 钩子,因为反应查询提供默认数据并有效处理缓存,并且您在返回数据之前将数据设置为 setState。不要使用 UseState 钩子,因为应该清楚 react 查询用于缓存、获取和更新数据,因此它会返回您更新的数据,并且在 react 中映射时始终使用

      {plants?.assigned_plant_code.map(
                       plantcode => {
                           return <Tag>{plantcode}</Tag>
                       }
                   ) }
      

      “?”是反应中的一个可选标记,它忽略assigned_plant_code.map 它是未定义的 你也可以这样做

      plants.length > 0  ? 
      {plants?.assigned_plant_code.map(
                       plantcode => {
                           return <Tag>{plantcode}</Tag>
                       }
                   ) }
          : <div>Error</div>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-20
        • 2023-03-13
        • 1970-01-01
        • 1970-01-01
        • 2020-08-21
        • 1970-01-01
        • 2020-09-04
        • 1970-01-01
        相关资源
        最近更新 更多