组件填充后没有任何东西出现

发布于 2025-02-09 04:25:41 字数 3165 浏览 0 评论 0原文

客户:React,MOBX 服务器:nodejs,mongodb

简短问题:

我有一系列元素填充在使用效果函数内,预期结果:应渲染数组的每个元素,实际结果:什么也没有发生。仅在VSCODE中的代码更改后才出现渲染。

尝试:将.map更改为.foreach,setState(... [arr])中的传播运算符的不同变化,甚至没有传播操作员,什么也不会改变。

信息:

friends.jsx部分,包含数组状态及其连接的所有内容,也包含填充功能。

  const [requestsFrom, setRequestsFrom] = useState([]) //contains id's (strings) of users that will be found in MongoDB
  const [displayRequestsFrom, setDisplayRequestsFrom] = useState([]) //should be filled by elements according to requestsFrom, see below

  const getUsersToDisplayInFriendRequestsFrom = () => {
    const _arr = [...displayRequestsFrom]
    requestsFrom.map(async(f) => {
      if (requestsFrom.length === 0) {
        console.log(`empty`) //this part of code never executes
        return
      } else {
        const _candidate = await userPage.fetchUserDataLite(f)
        _arr.push( //template to render UserModels (below)
          {
            isRequest: true,
            link: '#',
            username: _candidate.login,
            userId: _candidate._id
          }
        )
        console.log(_arr)
      }
    })
    setDisplayRequestsFrom(_arr)
    // console.log(`displayRequestsFrom:`)
    console.log(displayRequestsFrom) //at first 0, turns into 3 in the second moment (whole component renders twice, yes)
  }

渲染模板函数:

  const render = {
    requests: () => {
      return (
        displayRequestsFrom.map((friendCandidate) => {
          return (
            <FriendModel link={friendCandidate.link} username={friendCandidate.username} userId={friendCandidate.userId}/>
          )
        })
      )
    }
  }

使用效果:

  useEffect(() => {
    console.log(`requestsFrom.length === ${requestsFrom.length}`)
    if (!requestsFrom.length === 0) {
      return 
    } else if (requestsFrom.length === 0) {
      setRequestsFrom(toJS(friend.requests.from))
      if (toJS(friend.requests.from).length === 0) {
        const _arr = [...requestsFrom]
        _arr.push('0')
        setRequestsFrom(_arr)
      }
    }
      if (displayRequestsFrom.length < 1 && requestsFrom.length > 0) {
         getUsersToDisplayInFriendRequestsFrom()
         //displayRequestsFrom and requestsFrom lengths should be same
      }
    
  },
   [requestsFrom]
  )

JSX的一部分with Rendering:

    <div className={styles.Friends}>
      <div className={styles['friends-container']}>
           {render.requests()}
      </div>
    </div>

upd:我的console.log从开始时以正确的顺序输出:

requestsFrom.length === 0
requestsFrom.length === 3
displayRequestsFrom === 0
displayRequestsFrom === 3 

正如我们所能看到的,也不是从displayrequestsss安装和渲染的末尾,displayRequestsss neseversequestsss explaysRequestssss explaysrequestssss explayrequestssss唯一的displayrequestssss我找不到的问题 - 为什么在DisplayRequestsss中的3个模板也不会渲染它们,但是如果我按下forceupdate按钮(为调试目的而创建它,则在这里是:)

  const [ignored, forceUpdate] = React.useReducer(x => x + 1, 0);

  <button onClick={forceUpdate}>force update</button>

Client: React, mobx
Server: NodeJS, MongoDB

Short question:

I have an array of elements which fills inside of useEffect function, expected result: each element of array should be rendered, actual result: nothing happens. Render appears only after code changing in VSCode.

Tried: changing .map to .forEach, different variations of spread operator in setState(...[arr]) or even without spread operator, nothing changes.

Info:

Friends.jsx part, contains array state and everything that connected with it, also the fill-up function.

  const [requestsFrom, setRequestsFrom] = useState([]) //contains id's (strings) of users that will be found in MongoDB
  const [displayRequestsFrom, setDisplayRequestsFrom] = useState([]) //should be filled by elements according to requestsFrom, see below

  const getUsersToDisplayInFriendRequestsFrom = () => {
    const _arr = [...displayRequestsFrom]
    requestsFrom.map(async(f) => {
      if (requestsFrom.length === 0) {
        console.log(`empty`) //this part of code never executes
        return
      } else {
        const _candidate = await userPage.fetchUserDataLite(f)
        _arr.push( //template to render UserModels (below)
          {
            isRequest: true,
            link: '#',
            username: _candidate.login,
            userId: _candidate._id
          }
        )
        console.log(_arr)
      }
    })
    setDisplayRequestsFrom(_arr)
    // console.log(`displayRequestsFrom:`)
    console.log(displayRequestsFrom) //at first 0, turns into 3 in the second moment (whole component renders twice, yes)
  }

Render template function:

  const render = {
    requests: () => {
      return (
        displayRequestsFrom.map((friendCandidate) => {
          return (
            <FriendModel link={friendCandidate.link} username={friendCandidate.username} userId={friendCandidate.userId}/>
          )
        })
      )
    }
  }

useEffect:

  useEffect(() => {
    console.log(`requestsFrom.length === ${requestsFrom.length}`)
    if (!requestsFrom.length === 0) {
      return 
    } else if (requestsFrom.length === 0) {
      setRequestsFrom(toJS(friend.requests.from))
      if (toJS(friend.requests.from).length === 0) {
        const _arr = [...requestsFrom]
        _arr.push('0')
        setRequestsFrom(_arr)
      }
    }
      if (displayRequestsFrom.length < 1 && requestsFrom.length > 0) {
         getUsersToDisplayInFriendRequestsFrom()
         //displayRequestsFrom and requestsFrom lengths should be same
      }
    
  },
   [requestsFrom]
  )

Part of jsx with rendering:

    <div className={styles.Friends}>
      <div className={styles['friends-container']}>
           {render.requests()}
      </div>
    </div>

UPD: my console.log outputs in the right order from beginning:

requestsFrom.length === 0
requestsFrom.length === 3
displayRequestsFrom === 0
displayRequestsFrom === 3 

As we can see, nor requestsFrom, neither displayRequestsFrom are empty at the end of the component mounting and rendering, the only problem left I can't find out - why even with 3 templates in displayRequestsFrom component doesn't render them, but render if I press forceUpdate button (created it for debug purposes, here it is:)

  const [ignored, forceUpdate] = React.useReducer(x => x + 1, 0);

  <button onClick={forceUpdate}>force update</button>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

何以心动 2025-02-16 04:25:41

Pricipal回答

这里的问题是您正在执行.map方法内部的获取。
这样,您就不会等待获取完成(请参见评论)

错误的示例(带有澄清评论)

  const getUsersToDisplayInFriendRequestsFrom =  () => {
    const _arr = [...displayRequestsFrom];
    // we are not awating requestsFrom.map() (and we can't as in this example, cause .map is not async and don't return a Promise)
    requestsFrom.map(async (f) => { 
        const _candidate = await userPage.fetchUserDataLite(f)
        // This is called after setting the state in the final line :( 
        _arr.push( 
          {
            isRequest: true,
            link: '#',
            username: _candidate.login,
            userId: _candidate._id
          }
        )
    } )
    setDisplayRequestsFrom(_arr) // This line is called before the first fetch resolves.  
   // The _arr var is still empty at the time of execution of the setter
 }

要解决,您需要等待每个获取,然后再使用新数组更新状态。

为此,您的整个功能必须是异步的,并且您需要在循环中等待。

例如,此代码变成

  const getUsersToDisplayInFriendRequestsFrom =  async () => {  // Note the async keyword here
     const _arr = [...displayRequestsFrom]
     for (let f of requestsFrom) {
       const _candidate = await fetchUserData(f)
       _arr.push(
         {
           isRequest: true,
           link: '#',
           username: _candidate.login,
           userId: _candidate._id
         }
       )
     }
     setDisplayRequestsFrom(_arr)
 }

一样并行执行每个

  const getUsersToDisplayInFriendRequestsFrom =  async () => {  // Note the async keyword here
     const _arr = [...displayRequestsFrom]
     await Promise.all(requestsFrom.map((f) => { 
       return fetchUserData(f).then(_candidate => {
          _arr.push(
            {
              isRequest: true,
              link: '#',
              username: _candidate.login,
              userId: _candidate._id
            }
          )
      });
     }));
     setDisplayRequestsFrom(_arr);
 }

您也可以像其他问题

从未调用服务的

问题,似乎您正在映射您试图调用服务的空数组。

const getUsersToDisplayInFriendRequestsFrom = () => {
const _arr = [...displayRequestsFrom]
/* HERE */ requestsFrom.map(async(f) => {
  if (requestsFrom.length === 0) {
    return

如果数组(From)为空的数组(requestssfrom)(在USESTATE([])中初始化您在MAP方法中传递的函数永远不会被调用。

不确定您要确切地尝试做什么,但这应该是问题之一...


不要使用状态作为渲染组件

,您不使用状态来存储渲染的组件

 _arr.push(
          <FriendModel key={_candidate.id} isRequest={true} link='#' username={_candidate.login} userId={_candidate._id}/>
        )

,而是应该将数据映射在模板,然后为数据阵列中的每个元素提供一个组件。

例如:

function MyComponent() {
   const [myData, setMyData] = useState([{name: 'a'}, {name: 'b'}])

   return (<>
        {
         myData.map(obj => <Friend friend={obj} />)
         }
   </>)
}

不:

function MyComponent() {
   const [myDataDisplay, setMyDataDisplay] = useState([
         <Friend friend={{name: 'a'}} />, 
         <Friend friend={{name: 'b'}} />
   ])

   return <>{myDataDisplay}</>
}

不要使用useffect来初始化您的状态,

我想知道为什么您在 value useffect 中设置requests

您为什么不在usestate()内初始化requests的状态?

类似的东西,

const [requestsFrom, setRequestsFrom] = useState(toJS(friend.requests.from))

而不是检查useffect内的长度并填写

,以便您的使用效率可以变成类似的东西

useEffect(() => {
  if (displayRequestsFrom.length < 1 && requestsFrom.length > 0) {
     getUsersToDisplayInFriendRequestsFrom()
  }
},
 [requestsFrom]
)

PRICIPAL ANSWER

The problem here is that you are executing fetch inside .map method.
This way, you are not waiting for the fetch to finish (see comments)

Wrong Example (with clarification comments)

  const getUsersToDisplayInFriendRequestsFrom =  () => {
    const _arr = [...displayRequestsFrom];
    // we are not awating requestsFrom.map() (and we can't as in this example, cause .map is not async and don't return a Promise)
    requestsFrom.map(async (f) => { 
        const _candidate = await userPage.fetchUserDataLite(f)
        // This is called after setting the state in the final line :( 
        _arr.push( 
          {
            isRequest: true,
            link: '#',
            username: _candidate.login,
            userId: _candidate._id
          }
        )
    } )
    setDisplayRequestsFrom(_arr) // This line is called before the first fetch resolves.  
   // The _arr var is still empty at the time of execution of the setter
 }

To solve, you need to await for each fetch before updating the state with the new array.

To do this, your entire function has to be async and you need to await inside a for loop.

For example this code became

  const getUsersToDisplayInFriendRequestsFrom =  async () => {  // Note the async keyword here
     const _arr = [...displayRequestsFrom]
     for (let f of requestsFrom) {
       const _candidate = await fetchUserData(f)
       _arr.push(
         {
           isRequest: true,
           link: '#',
           username: _candidate.login,
           userId: _candidate._id
         }
       )
     }
     setDisplayRequestsFrom(_arr)
 }

You can also execute every fetch in parallel like this

  const getUsersToDisplayInFriendRequestsFrom =  async () => {  // Note the async keyword here
     const _arr = [...displayRequestsFrom]
     await Promise.all(requestsFrom.map((f) => { 
       return fetchUserData(f).then(_candidate => {
          _arr.push(
            {
              isRequest: true,
              link: '#',
              username: _candidate.login,
              userId: _candidate._id
            }
          )
      });
     }));
     setDisplayRequestsFrom(_arr);
 }

Other problems

Never Calling the Service

Seems you are mapping on an empty array where you are trying to call your service.

const getUsersToDisplayInFriendRequestsFrom = () => {
const _arr = [...displayRequestsFrom]
/* HERE */ requestsFrom.map(async(f) => {
  if (requestsFrom.length === 0) {
    return

If the array (requestsFrom) is empty ( as you initialized in the useState([]) ) the function you pass in the map method is never called.

Not sure what you are exactly trying to do, but this should be one of the problems...


Don't use state for rendered components

Also, you shoudn't use state to store rendered components

 _arr.push(
          <FriendModel key={_candidate.id} isRequest={true} link='#' username={_candidate.login} userId={_candidate._id}/>
        )

, instead you should map the data in the template and then render a component for each element in your data-array.

For example:

function MyComponent() {
   const [myData, setMyData] = useState([{name: 'a'}, {name: 'b'}])

   return (<>
        {
         myData.map(obj => <Friend friend={obj} />)
         }
   </>)
}

Not:

function MyComponent() {
   const [myDataDisplay, setMyDataDisplay] = useState([
         <Friend friend={{name: 'a'}} />, 
         <Friend friend={{name: 'b'}} />
   ])

   return <>{myDataDisplay}</>
}

Don't use useEffect to initialize your state

I'm wondering why you are setting the requestsFrom value inside the useEffect.

Why aren't you initializing the state of your requestsFrom inside the useState()?

Something like

const [requestsFrom, setRequestsFrom] = useState(toJS(friend.requests.from))

instead of checking the length inside the useEffect and fill it

So that your useEffect can became something like this

useEffect(() => {
  if (displayRequestsFrom.length < 1 && requestsFrom.length > 0) {
     getUsersToDisplayInFriendRequestsFrom()
  }
},
 [requestsFrom]
)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文