客户端:React,mobx

简短问题:

我有一个元素数组,它填充了useffect函数的内部,预期的结果是:数组中的每个元素都应该被渲染,实际的结果是:什么都没有发生.仅在VSCode中更改代码后才会显示渲染.

已try :更改.映射到.对于每一种情况,在设置状态(…[arr])中扩展运算符的不同变化,或者即使没有扩展运算符,也不会发生任何变化.

信息:

朋友.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的渲染部分:

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

UPD:我的控制台.从一开始就按正确的顺序记录输出:

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

正如我们所看到的,在组件装载和渲染结束时,displayRequestsFrom和requestsFrom都不为空,唯一剩下的问题是我找不到-为什么即使displayRequestsFrom组件中有3个模板也不渲染它们,但如果我按forceUpdate按钮(创建它是为了调试目的,这里是:)

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

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

推荐答案

主要答案

这里的问题是您正在内部执行fetch.映射方法.

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
 }

要解决此问题,您需要等待每次提取,然后再使用新数组更新状态.

要做到这一点,整个函数必须是异步的,并且需要在for循环中等待.

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)
 }

其他问题

从不调用服务

似乎您正在映射一个空数组,试图调用您的服务.

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

如果数组(requestsFrom)为空(当您在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}</>
}

Don't use useEffect to initialize your state

我想知道为什么要在useEffect中设置requestsFrom值.

为什么不在useState()中初始化requestsFrom的状态?

Something like

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

而不是判断useEffect内的长度并填充

So that your useEffect can became something like this

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

Javascript相关问答推荐

ChartJS:分组堆叠条形图渲染错误

同步功能在中间停止

JavaScript .Click()函数不起作用

如何在NightWatch.js测试中允许浏览器权限?

获取表格的左滚动位置

Vue:ref不会创建react 性属性

字节数组通过echo框架传输到JS blob

Plotly热图:在矩形上zoom 后将zoom 区域居中

在网页上添加谷歌亵渎词

当作为表达式调用时,如何解析方法decorator 的签名?

分层树视图

当Redux提供程序访问Reduxstore 时,可以安全地从Redux提供程序外部调用钩子?

我怎么才能得到Kotlin的密文?

使用Promise.All并发解决时,每个promise 的线性时间增加?

WhatsApp Cloud API上载问题:由于MIME类型不正确而导致接收&Quot;INVALID_REQUEST";错误

扩展类型的联合被解析为基类型

根据一个条件,如何从处理过的数组中移除一项并将其移动到另一个数组?

如何在 Select 文本时停止Click事件?

回溯替代方式

第一项杀死下一项,直到数组长度在javascript中等于1