Raghav Herugu
Raghav Herugu

Reputation: 546

Making axios call in render does not return anything

I am making an axios call in the render of my react native app.

render() {
    return (
      <View style={{flex: 1, backgroundColor: 'white'}}>
        <ScrollView
          style={styles.scrollView}
          contentContainerStyle={{justifyContent: 'center'}}>
          <Text style={styles.heading}>Checked Out Books</Text>
          {this.state.books.map((book) => {
            axios
              .get('http://localhost:3000/books/id/' + book)
              .then((res) => {
                console.log(res.data);
                return (
                  <View>
                    <Image
                      source={{uri: res.data.cover}}
                      height={150}
                      width={110}></Image>
                    <Text>{res.data.title}</Text>
                  </View>
                );
              })
              .catch((err) => {
                console.log(err);
                alert('Something went wrong.');
              });
          })}

When the call finishes and it gets to the return step, nothing is returned.

I don't know why this is happening. This segment of code is not returned:

<View>
  <Image
      source={{uri: res.data.cover}}
      height={150}
      width={110}></Image>
      <Text>{res.data.title}</Text>
</View>

Why is this?

Upvotes: 1

Views: 143

Answers (3)

Linda Paiste
Linda Paiste

Reputation: 42228

Per the comment that you are not returning anything from .map(), an arrow function either needs to have no brackets like (book) => axios.get( or have brackets and use the word return like (book) => { return axios.get(.

The return statement that you are looking at with the <View> element is the return for the .then() callback function, not for the whole .map() function.

But this is not going to solve your issue because the value that you would be returning from the .map() is a Promise, which is not something that you can print out in your component JSX.

@jamielarchin is correct that you cannot do data fetching inside your render() method. It needs to be inside of a lifecycle method like componentDidMount or componentDidUpdate or inside of a useEffect hook. Since you are just learning, I recommend that you learn with the most current approach which is to use a function component and useEffect.

I recommend that you handle the data fetching for each book in a separate Book component rather than dealing with fetching data in a loop. This is much simpler and it avoids having to refetch the entire list if just one book changes.

You can make that Book component be a function component and keep your current "list of books" component as a class component.

// the book id is the only prop
const Book = ({ id }) => {
  const [isError, setIsError] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  // save the book data here, once loaded
  const [data, setData] = useState();

  useEffect(
    () => {
      setIsLoading(true);
      axios
        .get('http://localhost:3000/books/id/' + id)
        .then((res) => setData(res.data))
        .catch((err) => setIsError(true))
        .finally(() => setIsLoading(false));
    },
    [id] // dependency on the book id
  );

  if (data !== undefined) {
    return (
      <View>
        <Image source={{ uri: data.cover }} height={150} width={110}></Image>
        <Text>{data.title}</Text>
        )}
      </View>
    );
  }
  // can show loading and error components too
  // but for now I'm just returning nothing
  return null;
};
render() {
    return (
      <View style={{flex: 1, backgroundColor: 'white'}}>
        <ScrollView
          style={styles.scrollView}
          contentContainerStyle={{justifyContent: 'center'}}>
          <Text style={styles.heading}>Checked Out Books</Text>
          {this.state.books.map((book) => <Book id={book} key={book}/>)}

Upvotes: 1

RalfiBahar
RalfiBahar

Reputation: 72

You shouldn't make an axios call inside render (it is possible with some adjustments however it is not a good practice). You should either use hooks (like useEffect) if you are in a functional component or use componentDidMount if you are in a class component (which I assume you are). Examples are shown below.

class App extends React.Component {
  constructor(){
    this.state = {
        bookIds: [], //You would fill this beforehand
        books: [],
      }
  }

  componentDidMount(){
    for(let i = 0; i < bookIds; i++){
      axios.get('http://localhost:3000/books/id/' + bookIds[i]).then((res) => {
         books.push(res); 
       }.catch((err) => {
           console.log(err);
           alert('Something went wrong.');
      });
    }
  }


  render() {
    return(
        <View style={{flex: 1, backgroundColor: 'white'}}>
        <ScrollView
          style={styles.scrollView}
          contentContainerStyle={{justifyContent: 'center'}}>
          <Text style={styles.heading}>Checked Out Books</Text>
          {this.state.books.map((book) => {
             return (
               <View>
                 <Image
                   source={{uri: book.data.cover}}
                   height={150}
                   width={110}>
                 </Image>
                 <Text>{book.data.title}</Text>
               </View>
            );
        })
      }
    );
  }
}

Would recommend you take a look at this: https://reactjs.org/docs/state-and-lifecycle.html.

There might be some issues with the brackets, Have a great day!

Upvotes: 0

jamielarchin
jamielarchin

Reputation: 1

Data fetching should be done from a lifecycle method for class components. You probably want to use componentDidMount: https://reactjs.org/docs/react-component.html#componentdidmount

Upvotes: 0

Related Questions