如何在React的render函数中使用异步等待(async await)?

45

我对后端nodejs中的async await非常熟悉,但遇到了一个情况,需要在前端使用它。

我正在获取对象数组,其中包含地点的lat lng。现在,使用react-geocode,我可以获取单个lat lng的地名,但我希望在map函数中使用它来获取所有地点的名称。由于这是一个async调用,所以我必须在那里使用async await

以下是代码:

import Geocode from "react-geocode";
render = async() => {
  const {
    phase,
    getCompanyUserRidesData
  } = this.props   
                      
  return (
    <div>
       <tbody>                   
        await Promise.all(_.get(this.props, 'getCompanyUserRidesData', []).map(async(userRides,index) => {
          const address = await Geocode.fromLatLng(22.685131,75.873468)
          console.log(address.results[0].formatted_address)                         
         return ( 
          <tr key={index}>
            <td>
            {address.results[0].formatted_address}
            </td>
            <td>Goa</td>
            <td>asdsad</td>
            <td>{_.get(userRides,'driverId.email', '')}</td>
            <td>{_.get(userRides,'driverId.mobile', '')}</td>
          </tr>
        )
        }))
      </tbody>
    </div>
  )
}

但是当我在这里使用map函数时,如果同时使用async关键字,它不会返回任何结果。请问有人可以帮助我找出问题所在吗?


4
render 函数将 UI 渲染到浏览器上,因此这不是您想进行异步调用的地方。 - Agney
1
@Profer 是的。将其设置为父组件的状态,条件渲染子组件一旦状态被设置(即异步数据到达)。这在任何React应用程序中都是非常标准的,而且我IRC在官方教程中也很早就介绍了(我可能记错了)。 - Jared Smith
1
@JaredSmith,您能否举个例子呢?顺便问一下,IIRC是什么意思? - Profer
1
@Profer 如果我没记错的话 - Jared Smith
@Galupuf 是的,我尝试过了,但仍然没有运气。 - Profer
显示剩余5条评论
2个回答

66

您应该始终将获取数据的关注点与显示数据的关注点分开。这里有一个父组件,通过 AJAX 获取数据,然后在数据到达时有条件地呈现一个纯函数子组件。

class ParentThatFetches extends React.Component {
  constructor () {
    this.state = {};
  }

  componentDidMount () {
    fetch('/some/async/data')
      .then(resp => resp.json())
      .then(data => this.setState({data}));
  }

  render () {
    {this.state.data && (
      <Child data={this.state.data} />
    )}
  }
}

const Child = ({data}) => (
  <tr>
    {data.map((x, i) => (<td key={i}>{x}</td>))}
  </tr>
);

我实际上没有运行它,因此可能会有一些小错误,如果您的数据记录具有唯一的ID,则应使用这些ID作为key属性而不是数组索引,但您可以理解意思。

更新

使用hooks相同但更简单和更短:

const ParentThatFetches = () => {
  const [data, updateData] = useState();
  useEffect(() => {
    const getData = async () => {
      const resp = await fetch('some/url');
      const json = await resp.json()
      updateData(json);
    }
    getData();
  }, []);

  return data && <Child data={data} />
}

2
谢谢,如果它有效,我会让你知道的。在此期间+1。 - Profer
2
谢谢!在深夜卡住时,这对我来说是一个很大的帮助! - Charitra Agarwal

4
使用下面的包装函数delayed_render(),您可以在React组件函数内编写异步代码:
function delayed_render(async_fun, deps=[]) {
    const [output, setOutput] = useState()
    useEffect(async () => setOutput(await async_fun()), deps)
    return (output === undefined) ? null : output
}

此包装器执行延迟渲染:在初始渲染尝试时返回null(以跳过对此特定组件的渲染),然后通过给定的async_fun()异步计算出正确的渲染输出(使用useEffect())并调用重新渲染来将最终结果注入到DOM中。使用此包装器的方法很简单:

function Component(props) {
    return delayed_render(async () => { /* any rendering code with awaits... */ })
}

例如:

function Component(props) {
    return delayed_render(async () => {
        const resp = await fetch(props.targetURL)     // await here is OK!
        const json = await resp.json()
        return <Child data={json} />
    })
}

更新:添加了deps参数。如果您的async_fun依赖于props或state变量,则必须在deps中列出所有这些变量,以允许重新渲染。请注意,这里不能传递deps=null(始终重新渲染),因为output也是状态变量,并且会被隐含地包含在依赖项中,在async_fun调用完成后会导致无限重新渲染。

此解决方案受到Jared Smith的启发,并是其泛化版本。


1
感谢您提供这个更新的答案!今天对我很有帮助。 - Caleb Jay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接