1

I have a userId array and I need to show the list of names related to that array. I want to call API call inside the render method and get the username. But this is not working. How can I fix this issue?

Below is my render method:

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

Below is the renderName function:

renderName = (userId) => {
    axios.get(backendURI.url + '/users/getUserName/' + userId)
         .then(res => <div>{res.data.name}</div>)
}
m02ph3u5
  • 3,022
  • 7
  • 38
  • 51
shayanmalinda
  • 1,925
  • 3
  • 12
  • 23

5 Answers5

1

Generally, you do not change state or fetch data in the render method directly. State is always changed by actions/events (clicks, input or whatever). The render method is called everytime a prop/state changes. If you change the state within the render method directly, you end up having an infinite loop.

You should use the lifecycle methods or hooks to load data from an api. Here's an example from the official React FAQ: https://reactjs.org/docs/faq-ajax.html

skas102
  • 31
  • 4
1

Basically you cannot use asynchronous calls inside a render because they return a Promise which is not valid JSX. Rather use componentDidMount and setState to update the users array with their names.

Lance
  • 701
  • 6
  • 25
1

This will not render anything as the API calls are asynchronous and since renderName function isn't returning anything, it'll return undefined.

You should create a function, which will call api for all the userIds and update in state

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

}

Now you can call this function in either componentDidMount or componentDidUpdate, whenever users data is available.

And finally, you can iterate over names directly and render them

<div>
  {this.state.names.map((name) => {
    return <div> {name} </div>;
  })}
</div>
Siddharth
  • 1,200
  • 8
  • 13
0

You could make user name it's own component:

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>
HMR
  • 37,593
  • 24
  • 91
  • 160
0
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>
  );
}
Vahid Alimohamadi
  • 4,900
  • 2
  • 22
  • 37