API 在 React inside map 中调用 render 方法

API call in render method in React inside map

我有一个 userId 数组,我需要显示与该数组相关的名称列表。我想在 render 方法中调用 API 并获取用户名。但这是行不通的。我该如何解决这个问题?

下面是我的render方法:

render(){
    ...
    return(
        <div>
            {this.state.users.map(userId => {
                return (
                    <div> {this.renderName(userId )} </div>
                )
            })}
        </div>
    )
    ...
}

下面是 renderName 函数:

renderName = (userId) => {
    axios.get(backendURI.url + '/users/getUserName/' + userId)
         .then(res => <div>{res.data.name}</div>)
}

通常,您不会直接在渲染方法中更改状态或获取数据。状态总是由 actions/events(点击、输入或其他)改变。每次 prop/state 更改时都会调用 render 方法。如果直接在 render 方法中更改状态,最终会出现无限循环。

您应该使用生命周期方法或挂钩从 api 加载数据。这是官方 React FAQ 中的示例:https://reactjs.org/docs/faq-ajax.html

基本上你不能在渲染中使用异步调用,因为它们 return 一个 Promise,它不是有效的 JSX。而是使用 componentDidMount 和 setState 用他们的名字更新用户数组。

这不会呈现任何东西,因为 API 调用是异步的,并且由于 renderName 函数没有 return 任何东西,它会 return 未定义。

您应该创建一个函数,它将为所有用户 ID 调用 api 并更新状态

getNames = () => {
    const promises = [];
    this.state.users.forEach((userId) => {
        promises.push(axios.get(backendURI.url+'/users/getUserName/'+userId));
    })

    // Once all promises are resolved, update the state
    Promise.all(promises).then((responses) => {
        const names = responses.map((response) => response.data.names);
        this.setState({names});
    })

}

现在只要 users 数据可用,您就可以在 componentDidMountcomponentDidUpdate 中调用此函数。

最后,您可以直接遍历名称并呈现它们

<div>
  {this.state.names.map((name) => {
    return <div> {name} </div>;
  })}
</div>

您可以将用户名设为自己的组件:

const request = (id) =>
  new Promise((resolve) =>
    setTimeout(resolve(`id is:${id}`), 2000)
  );
const UserName = React.memo(function User({ userId }) {
  const [name, setName] = React.useState('');
  React.useEffect(() => {
    //make the request and set local state to the result
    request(userId).then((result) => setName(result));
  }, [userId]);
  return <div> {name} </div>;
});
class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      users: [1, 2],
    };
  }
  render() {
    return (
      <ul>
        {this.state.users.map((userId) => (
          <UserName key={userId} userId={userId} />
        ))}
      </ul>
    );
  }
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


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

export default ()=> {
  let [users,setUsers] = useState([]);

  useEffect(()=>{
    let fetchUsersInfoRemote = Promise.all([...Array(10)].map(async (_,index)=>{
        try {
          let response = await axios.get(`https://jsonplaceholder.typicode.com/posts/${index+1}`);
          return response.data;
        }
        catch(error) {
          return ;
        }
    }));

    fetchUsersInfoRemote.then(data=> setUsers(data));
  },[]);

  return (
    <div className="App">
      <ul>
        {
          users.map(user=>(<li><pre>{JSON.stringify(user,null,2)}</pre></li>))
        }
      </ul>
    </div>
  );
}