【问题标题】:I am getting an error in my map function returning React component我在返回 React 组件的地图函数中遇到错误
【发布时间】:2021-11-06 13:14:28
【问题描述】:

这是我的代码,其中我在对象数组上运行映射函数,每个对象有 2 个条件检查,第一个条件中有一个异步操作,然后是 return 语句。错误是这样的-

未捕获的错误:对象作为 React 子项无效(找到:[object Promise])。如果您打算渲染一组子项,请改用数组。

<ChannelList
          style={{
            padding: '8px',
          }}
        >
          {Promise.all(channelList.map((channel) => 
            {
              console.log(channel);
              const channelMetaData = JSON.parse(channel?.Metadata);
              //If it is one-to-one chat
              console.log(channelMetaData);  
              if(channelMetaData?.isOneToOne){
                listChannelMemberships(channel.ChannelArn,userId).then(async (res)=>{
                  if(res[0].Member.Name==userData.username){
                    console.log("I am this user",res[0].Member.Name);
                    var otherPerson = res[1].Member.Name;
                  }else if(res[1].Member.Name==userData.username){
                    console.log("I am this user",res[1].Member.Name);
                    var otherPerson = res[0].Member.Name;
                  }
                  console.log(otherPerson);
                  return <ChannelItem
                    key={channel.ChannelArn}
                    name={otherPerson}
                    actions={loadUserActions(userPermission.role, channel)}
                    isSelected={channel.ChannelArn === activeChannel.ChannelArn}
                    onClick={e => {
                      e.stopPropagation();
                      channelIdChangeHandler(channel.ChannelArn);
                    }}
                    unread={unreadChannels.includes(channel.ChannelArn)}
                    unreadBadgeLabel="New"
                    >
                    </ChannelItem>
                  })
             }
             else{
              return <ChannelItem
              key={channel.ChannelArn}
              name={channel.Name}
              actions={loadUserActions(userPermission.role, channel)}
              isSelected={channel.ChannelArn === activeChannel.ChannelArn}
              onClick={e => {
                e.stopPropagation();
                channelIdChangeHandler(channel.ChannelArn);
              }}
              unread={unreadChannels.includes(channel.ChannelArn)}
              unreadBadgeLabel="New"
              >
              </ChannelItem>
             }
            }
            ))}
        </ChannelList>

【问题讨论】:

  • 如果您将此代码与组件需要呈现的内容分离为辅助函数(如 API 调用和操作),您会更容易理解和调试。

标签: javascript reactjs es6-promise asynchronous-javascript


【解决方案1】:

正如错误所说,对象在 React 中不是有效的子对象。您正在使用 Promise.all(一个 Promise 对象)的结果作为该 JSX 表达式的结果:

{Promise.all(/*...*/)}

相反,您需要等待来自Promise.all 的承诺通过结果数组来实现。渲染时不能这样做,需要在渲染之外进行,然后根据结果设置状态,改为渲染该状态。

这是一个简单的例子(看看&lt;script type="text/babel"&gt; 脚本块中的代码;遗憾的是,Stack Snippets use an out of date version of Babel 所以我们不能正常执行):

<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

<script type="text/babel" data-presets="es2017,react">
const {useState, useEffect} = React;

// Stand-in for actual retriever function
function getThing(id, signal) {
    //                ^^^^^^−−−− this is so you can pass
    // it to anything that supports signals to cancel the
    // work if the component unmounts.
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`Done getting thing #${id}`);
            resolve({id});
        }, Math.floor(Math.random() * 800));
    });
}

const Thing = ({thing}) => <div>{thing.id}</div>;

const Example = () => {
    const [things, setThings] = useState(null);

    // Load on mount
    useEffect(() => {
        const controller = new AbortController();
        const {signal} = controller;
        (async () => {
            try {
                const things = await Promise.all([1, 2, 3].map(async (id) => {
                    console.log(`Getting thing #${id}...`);
                    const thing = await getThing(id, {signal});
                    return <Thing thing={thing} />;
                }));
                setThings(things);
            } catch (error) {
                // ...handle/report error...
                console.error(error.message, error.stack);
            }
        })();
        return () => {
            controller.abort();
        }
    }, []); // [] = just on mount

    // Return
    return <div>
        {things === null && <em>Loading...</em>}
        {things}
    </div>;
};

ReactDOM.render(<Example />, document.getElementById("root"));
</script>
<script src="https://unpkg.com/regenerator-runtime@0.13.2/runtime.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.10.3/babel.min.js"></script>

该版本将实际的 React 元素存储在状态中。相反,您可以只存储您需要的信息(对象或其他),然后根据需要渲染元素:

<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

<script type="text/babel" data-presets="es2017,react">
const {useState, useEffect} = React;

// Stand-in for actual retriever function
function getThing(id, signal) {
    //                ^^^^^^−−−− this is so you can pass
    // it to anything that supports signals to cancel the
    // work if the component unmounts.
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`Done getting thing #${id}`);
            resolve({id});
        }, Math.floor(Math.random() * 800));
    });
}

const Thing = ({thing}) => <div>{thing.id}</div>;

const Example = () => {
    const [things, setThings] = useState(null);

    // Load on mount
    useEffect(() => {
        const controller = new AbortController();
        const {signal} = controller;
        (async () => {
            try {
                const things = await Promise.all([1, 2, 3].map(async (id) => {
                    console.log(`Getting thing #${id}...`);
                    const thing = await getThing(id, {signal});
                    return thing;
                }));
                setThings(things);
            } catch (error) {
                // ...handle/report error...
                console.error(error.message, error.stack);
            }
        })();
        return () => {
            controller.abort();
        }
    }, []); // [] = just on mount

    // Return
    return <div>
        {things === null && <em>Loading...</em>}
        {things && things.map(thing => <Thing thing={thing} />)}
    </div>;
};

ReactDOM.render(<Example />, document.getElementById("root"));
</script>
<script src="https://unpkg.com/regenerator-runtime@0.13.2/runtime.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.10.3/babel.min.js"></script>

map 回调也有问题。例如,if(channelMetaData?.isOneToOne){ 中的真正分支不会返回任何内容。在then 回调中有一个return,但在map 回调中没有。您可能会发现对map 回调使用async 函数并使用await 而不是.then 很有用。这可能看起来像这样:

// (In an `async` function)
const result = await Promise.all(channelList.map(async (channel) => {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^
    console.log(channel);
    const channelMetaData = JSON.parse(channel?.Metadata);
    //If it is one-to-one chat
    console.log(channelMetaData);
    if (channelMetaData?.isOneToOne) {
        const res = await listChannelMemberships(channel.ChannelArn, userId);
// −−−−−−−−−−−−−−−−−^^^^^
        let otherPerson; // Declare it once (and `var` is deprecated)
        if (res[0].Member.Name == userData.username) {
            console.log("I am this user", res[0].Member.Name);
            otherPerson = res[1].Member.Name;
        } else if (res[1].Member.Name == userData.username) {
            console.log("I am this user", res[1].Member.Name);
            otherPerson = res[0].Member.Name;
        }
        console.log(otherPerson);
        return <ChannelItem
            key={channel.ChannelArn}
            name={otherPerson}
            actions={loadUserActions(userPermission.role, channel)}
            isSelected={channel.ChannelArn === activeChannel.ChannelArn}
            onClick={e => {
                e.stopPropagation();
                channelIdChangeHandler(channel.ChannelArn);
            }}
            unread={unreadChannels.includes(channel.ChannelArn)}
            unreadBadgeLabel="New"
        >
        </ChannelItem>;
    } else {
        return <ChannelItem
            key={channel.ChannelArn}
            name={channel.Name}
            actions={loadUserActions(userPermission.role, channel)}
            isSelected={channel.ChannelArn === activeChannel.ChannelArn}
            onClick={e => {
                e.stopPropagation();
                channelIdChangeHandler(channel.ChannelArn);
            }}
            unread={unreadChannels.includes(channel.ChannelArn)}
            unreadBadgeLabel="New"
        >
        </ChannelItem>;
    }
}));

【讨论】:

  • 我已经编辑了问题中的代码,现在您可以看到 Promise.all 在 ChannelList 组件内,那么如何在 ChannelList 组件内显示 Promise.all 的结果?我明白我应该使用 await。
  • @ArunPratapSingh - 基本上就是我在示例中展示的方式。 ChannelList 需要成为有状态的,并处于等待信息的状态,然后是拥有信息的状态。 (要么,要么它的父级需要将信息作为道具传递给它,甚至在它[父级] 获得信息之前都不渲染它。)
  • 好的,知道了。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-07
  • 2018-04-24
  • 2019-09-21
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 2020-01-23
相关资源
最近更新 更多