简体   繁体   中英

How to make multiple axios.get() requests with componentDidMount() and assign a response value of the 1st to the 2nd?

I am trying to build a web application with Wordpress REST API.

I am making an initial GET request to an endpoint and parsing through the res.data to get some values. But, one of the values featured_media is a parameter for the 2nd GET request I am trying to make. I am finding it difficult to get this value out of that state onto the second GET request.

Here are the states.

state = {
        graduatepost: {},
        category: '',
        featured_media: '',
        imgURL: '',
        isLoaded: false
    }

Here is componentDidMount()

componentDidMount() {
        const { featured_media } = this.props;

        axios.get(`http://localhost:8000/wp-json/wp/v2/blog/${this.props.match.params.id}`)
            .then(res => this.setState({
                graduatepost: res.data,
                category: res.data.categories[0],
                featured_media: res.data.featured_media,
                isLoaded: true
            }))
            .catch(err => console.log(err));

        const getImageURL = axios.get(`http://localhost:8000/wp-json/wp/v2/media/${featured_media}`);

        Promise.all([getImageURL]).then(res => {
            this.setState({
                imgURL: res[0].data.media_details.sizes.full.source_url,
                isLoaded: true
            });
        });
    }

1st GET request: http://localhost:8000/wp-json/wp/v2/blog/${this.props.match.params.id}

2nd GET request: http://localhost:8000/wp-json/wp/v2/media/${featured_media}

As you can see the 2nd request requires the value featured_media which is in the response of the 1st GET request.

I am rendering the component like this.

render() {
        const { graduatepost, category, isLoaded, featured_media, imgURL } = this.state;
        if(isLoaded) {
            return (
                <Styles>
                    <Fragment>
                        <Link to='/graduate-posts'>Go Back</Link> // Ignore this
                        <hr />
                        <h1>{graduatepost.title.rendered}</h1>
                        <div dangerouslySetInnerHTML={{__html: graduatepost.content.rendered}}></div>
                        <h4>Category: {category}</h4>
                        <h4>featured_media: {featured_media}</h4>
                        <h4>imgURL: {imgURL}</h4>
                    </Fragment>
                </Styles>
            )
        }
        return <h3>Loading...</h3> // Ignore this
    }

When I do the render the component. There is a 404 console error for the 2nd GET request, which states.

GET http://localhost:8000/wp-json/wp/v2/media/undefined 404 (Not Found)
Uncaught (in promise) Error: Request failed with status code 404
    at createError (createError.js:16)
    at settle (settle.js:17)
    at XMLHttpRequest.handleLoad (xhr.js:61)

I am assuming this is because featured_media is empty/undefined but I cannot figure out how to extract that value from the 1st GET request, response.

This may seem like an obvious one but I'm relatively new to working with React.js and APIs together. Your help would be greatly appreciated.

Thank you.

Have you tried Async function? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

async componentDidMount() {
        ....
        await axios.get ...
        ....
}

The best way to access the setted data immediately is to use callback .

this.setState accept the callback as its second argument ( setState(updater, [callback]) ), so we should make our second request in our callback statement.

Your code should be something like this:

axios
  .get(`http://localhost:8000/wp-json/wp/v2/blog/${this.props.match.params.id}`)
  .then((res) =>
    this.setState(
      {
        graduatepost: res.data,
        category: res.data.categories[0],
        featured_media: res.data.featured_media,
        isLoaded: true,
      },
      () =>
        axios
          .get(
            `http://localhost:8000/wp-json/wp/v2/media/${this.state.featured_media}`
          )
          .then((res) => {
            this.setState({
              imgURL: res[0].data.media_details.sizes.full.source_url,
              isLoaded: true,
            })
          })
    )
  )
  .catch((err) => console.log(err))

Maybe request it in the response of the 1st axios.get . The reason it isn't working is because this.setState is an async function in React so it's undefined when you access it immediately below.

Try something like:

axios.get(`http://localhost:8000/wp-json/wp/v2/blog/${this.props.match.params.id}`)
  .then((res) => {
    const state = {
             graduatepost: res.data,
             category: res.data.categories[0],
             featured_media: res.data.featured_media,
             isLoaded: true
    }
    this.setState(state)
    return axios.get(`http://localhost:8000/wp-json/wp/v2/media/${state.featured_media}`);
  })
  .then((res) => {
    // do something with res
  })
  .catch((err) => {
    // handle err
  });

I have prepared one example where it shows all users and if you click to see posts button, it will show all the posts for that user.

App.js

class App extends React.Component {
    render() {
        return (
            <Router>
                <div>
                    <ul>
                        <li>
                            <Link to="/">Home</Link>
                        </li>
                        <li>
                            <Link to="/posts">Posts</Link>
                        </li>
                    </ul>
                    <hr/>
                    <Switch>
                        <Route exact path="/">
                            <UserList/>
                        </Route>
                        <Route path="/posts">
                            <PostListPageByUser/>
                        </Route>
                    </Switch>
                </div>
            </Router>
        );
    }
}
export default App;

UserList Component

import React from 'react';
import axios from 'axios';
import PostListPageByUser from "./PostListPageByUser";
import {withRouter} from "react-router-dom";

class UserList extends React.Component {
    state = {
        users: [],
        showPostList: false,
        user: {}
    };

    componentDidMount() {
        axios.get(`https://jsonplaceholder.typicode.com/users`)
            .then(res => {
                const users = res.data;
                console.log(users);
                this.setState({users});
            })
    }

    handleClick = (user) => {
        console.log(user);
        this.setState({showPostList: true, user: user});
        this.props.history.push({
            pathname: '/posts',
            user: user
        });
    };

    render() {
        return (
            <div>
                <ul>
                    {this.state.users ? this.state.users.map(user => <div key={user.id}>
                        <span style={{minWidth: 400}}>{user.name} </span>
                        <button onClick={() => {
                            this.handleClick(user)
                        }}>See Posts
                        </button>
                    </div>) : null}
                </ul>
                {this.state.showPostList ? <PostListPageByUser user={this.state.user}/> : null}
            </div>
        )
    }
}

export default withRouter(UserList);

PostListByUser Component

import React from "react";
import axios from 'axios';
import {withRouter} from "react-router-dom";

class PostListPageByUser extends React.Component {
    signal = axios.CancelToken.source();
    state = {
        posts: [],
    };

    componentDidMount() {
        if(!this.props.location.user){
            alert('Please click see posts button');
            this.props.history.push('/');
            return;
        }
        axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${this.props.location.user.id}`, {
            cancelToken: this.signal.token,
        })
            .then(res => {
                this.setState({posts: res.data});
                console.log(res.data, 'Posts');
            }).catch(err => {
            console.log(err);
        });
    }

    render() {
        return (
            <div>
                <ul>
                    {
                        this.state.posts ? this.state.posts.map(post => <li key={post.id}>{post.title}</li>) : null
                    }
                </ul>
            </div>
        )
    }
}

export default withRouter(PostListPageByUser);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM