在内部提取错误setState

时间:2018-08-25 14:56:36

标签: javascript reactjs

我在解决此react.js时遇到问题

loadFromServer(pageSize) {

        fetch('http://localhost:8080/api/employees')
        .then(response => {
            return fetch('http://localhost:8080/api/profile/employees',
            {
                headers: new Headers({
                'Accept': 'application/schema+json'
              })
            }).then(schema => {
                this.scheme =  schema;
                return response.json();
            }   
            )

       })
        .then(response =>             
            this.setState(
                {
                employees: response._embedded.employees,
                attributes: Object.keys(this.scheme.json().properties),
                pageSize: pageSize,
                links: response._links}                      
            )
        );          
    }

在此部分

attributes: Object.keys(this.scheme.json().properties),

总是返回(承诺)TypeError: Cannot convert undefined or null to object.

如果我放console.log(this.scheme.json()),我可以看到Promise,但是为什么在setState里面却得到空对象?

4 个答案:

答案 0 :(得分:2)

这里有几个问题:

  • 主要的原因是this.schema.json()返回了一个承诺(如您从console.log所知)。承诺没有properties属性,因此您要将undefined传递给Object.keys,这会给您该错误。
  • 您也不会以两种不同的方式检查来自fetch的错误:您不是在检查.ok(这是常见的错误I've posted about it on my anemic little blog),而是不检查承诺是否被拒绝。

您还在做一些不必要的Promise嵌套,可能会使您的fetch调用更多地重叠。

首先,由于您似乎经常获取JSON,因此建议您为它提供一个实用程序功能:

function fetchJSON(...args) {
    return fetch(...args)
        .then(response => {
            if (!response.ok) {
                throw new Error('HTTP error ' + response.status);
            }
            return response.json();
        });
}

请注意.ok支票。

然后,同样在“将问题分解成小块”类别中,我将拥有一个fetchSchema函数:

function fetchSchema(url) {
    return fetchJSON(url, {
        headers: new Headers({
            'Accept': 'application/schema+json'
        })
    });
}

然后,loadFromServer可以使用Promise.all和解构来并行运行操作:

// (I assume this is in a `class` or object initializer, as it doesn't have `function` in front of it)
loadFromServer(pageSize) {
    Promise.all(
        fetchJSON('http://localhost:8080/api/employees'),
        fetchSchema('http://localhost:8080/api/profile/employees')
    )
    .then(([empResponse, schema]) => {
        this.schema = schema;
        this.setState({
            employees: empResponse._embedded.employees,
            attributes: Object.keys(schema.properties),
            pageSize: pageSize,
            links: empResponse._links
        })
    )
    .catch(error => {
        // Do something with the error
    });
}

请注意.catch,因为您没有从loadFromServer返回承诺。 (如果要消除错误,请在return前面添加Promise.all,然后将.catch移至调用代码。)


旁注:您的代码已使用

this.scheme =  schema;

请注意,左侧的属性为scheme(带最后一个e),但变量为schema(带最后一个a)。我认为您的意思是schema,因此我已经在上面包含了该更改,但是如果该属性确实应该为this.scheme,则需要进行调整。或者,如果除了loadFromServer中的代码之外,您不需要该属性,请完全删除该行。

答案 1 :(得分:2)

我认为您应该使用Promise.all并行运行两个请求,然后检索两个响应(通过response.json()返回Promise的方式,这就是为什么您在您的代码):

loadFromServer(pageSize) {
    Promise.all([
        fetch('http://localhost:8080/api/employees')
        .then(response => {
           if (!response.ok) throw Error(response.statusText);
           return response.json();
        ),
        fetch('http://localhost:8080/api/profile/employees')
        .then(response => {
           if (!response.ok) throw Error(response.statusText);
           return response.json();
        ),
    ]).then(responses => {
        this.setState({
          employees: responses[0]._embedded.employees,
          attributes: Object.keys(responses[1].properties),
          pageSize: pageSize,
          links: responses[0]._links
        })
    }).catch(error => {...})        
}

答案 2 :(得分:1)

Fetch API returns a promise中的响应json()方法。因此,fetch请求应与.then(response => response.json())始终保持链接,以获取普通对象。

平淡无奇的承诺可能会导致更可靠的控制流程。由于使用了两个请求的响应,因此这将需要嵌套then回调或通过then链传递另一个响应。 async可能有用,因为它可以方便地解决展平问题:

async loadFromServer(pageSize) {
    const employeesResponse = await fetch('http://localhost:8080/api/employees', {
      headers: new Headers({ 'Accept': 'application/schema+json' })
    });
     const employees = await employeesResponse.json();

    const schemeResponse = await fetch('http://localhost:8080/api/profile/employees', {
      headers: new Headers({ 'Accept': 'application/schema+json' })
    });
    const scheme = await schemeResponse.json();

    this.setState({
        employees: employees._embedded.employees,
        attributes: Object.keys(scheme.properties),
        pageSize: pageSize,
        links: response._links
    });
}

由于请求彼此不依赖,因此可以与Promise.all并行执行。

async loadFromServer(pageSize) {
    const employeesPromise = fetch('http://localhost:8080/api/employees', {
      headers: new Headers({ 'Accept': 'application/schema+json' })
    })
    .then(res => res.json());

    const schemePromise = fetch('http://localhost:8080/api/profile/employees', {
      headers: new Headers({ 'Accept': 'application/schema+json' })
    })
    .then(res => res.json());

    const [employees, scheme] = await Promise.all([employeesPromise, schemePromise]);

    this.setState({
        employees: employees._embedded.employees,
        attributes: Object.keys(scheme.properties),
        pageSize: pageSize,
        links: response._links
    });
}

答案 3 :(得分:1)

我认为您需要类似的东西:

loadFromServer(pageSize) {
  fetch('http://localhost:8080/api/employees')
    .then(response => {
      return fetch('http://localhost:8080/api/profile/employees', {
        headers: new Headers({
          'Accept': 'application/schema+json'
        })
      }).then(schema => {
        schema.json().then(data => {
          this.scheme = data
        })
      });
      return response.json();
    })
    .then(response =>
      this.setState({
        employees: response._embedded.employees,
        attributes: Object.keys(this.scheme.properties),
        pageSize: pageSize,
        links: response._links
      })
    );
}