簡體   English   中英

如何在prop更改中呈現返回的數據

[英]how to render returned data on prop change in react

我在弄清楚如何獲取API調用以重新呈現到屏幕時遇到了麻煩。 我有一個apiCall函數,它傳遞this.state並通過傳遞的ref更改狀態,但它不會觸發道具更改的重新渲染。

searchBody.js

 class SearchBody extends Component {
constructor(props) {
      super(props)
      const queryString = require('query-string');
      const queryTerm = queryString.parse(this.props.location.search);
      this.state = { urlSearchTerm: queryTerm.search,
                      searchTerm: '',
                      loaded: false,
                      buttonClicked: null,
                      apiData: [],
                      tableHeaders: [],
                      tableRows: []
                    }
      // check if URL has search term if so pass term for apiCall
      if (this.state.urlSearchTerm) {
         this.state.searchTerm = this.state.urlSearchTerm
      }

      this.handleChange = this.handleChange.bind(this)
      this.handleSubmit = this.handleSubmit.bind(this)
    }


// capture input text field to state variable
handleChange = searchTerm => event => {
  this.setState({ searchTerm: event.target.value })
  //console.log(this.state.searchTerm)
  }

// handle form submission
handleSubmit = (event) => {
  console.log('Inside HandleSubmit')
  console.log('button clicked update url to /?search=' + this.state.searchTerm)
  this.props.history.push('/?search=' + this.state.searchTerm);
  this.setState({buttonClicked: true})
  event.preventDefault();
}
// load search from API if search term is in URL
componentDidMount() {
  console.log('Inside compDidMount')
  if (this.state.urlSearchTerm){
      this.setState({apiData: apiCall(this.state)})
  }
 }

render() {
  const { classes } = this.props;

  let table = ''
  //check if API has loaded data and show results if true
if (this.state.loaded){
  if (this.state.apiData.length === 0 && this.state.buttonClicked){
    table = 'No Results Found'
    //reset search button State
    this.setState({buttonClicked: false})
  } else {
    table = <TableData tableHead={this.state.tableHeaders} tableData={this.state.tableRows} />
    //reset search button State
    this.setState({buttonClicked: false})
  }
}
    return (

      <Fragment>
      <hr/>
       <form /*className={classes.container}*/ noValidate autoComplete="off" onSubmit={this.handleSubmit} >
          <TextField
              id="search"
              label="Search field"
              type="search"
            /*  className={classes.textField}*/
              margin="normal"
              onChange={this.handleChange('search')}
            />
            <Button color='primary' letiant="outlined" type="submit" >Search DB</Button>
        </form>

          <h1>Results: </h1>
              {table}
        </Fragment>
          )
  }

}
export default SearchBody

methods.js

// break API data into arry of data for table component rows.
export const parseTableHeaders = input => {
  // console.log(input)
  if (input !== undefined && input.length !== 0) {
  let head = []
  for(let key in input[0]){  head.push(key);}
  //console.log(head)
  return head
}
}

///break API data into array of headers for table component
export const parseTableRows = (input) => {
  let rows = [];
  for(let o in input) {
      rows.push(Object.values(input[o]));
  }
  //console.log(head)
  return rows
}


//get api data from AWS
export function  apiCall(props) {
  const searchTerm = props.searchTerm
  let apigClientFactory = require('aws-api-gateway-client').default;

  const config =  {
                //apiKey: 'xxxx',
                invokeUrl:'https://xxxx.execute-api.us-east-2.amazonaws.com'
                }

  let apigClient = apigClientFactory.newClient(config);
  let params = {
        //This is where any header, path, or querystring request params go. The key is the parameter named as defined in the API
        //  userId: '1234',
        search_keyword: searchTerm
      };
      // Template syntax follows url-template https://www.npmjs.com/package/url-template
  let pathTemplate = '/beta/testDB'
  let method = 'GET';
  let additionalParams = {
      //If there are any unmodeled query parameters or headers that need to be sent with the request you can add them here
      headers: { },
      queryParams: {
        search_keyword: searchTerm
              }
    }
    apigClient.invokeApi(params, pathTemplate, method, additionalParams)
      .then(function(result){
          //This is where you would put a success callback
          console.log('apiCall Returned. searchTerm; ', searchTerm)
          console.log(result)
          props.loaded = true
          props.tableHeaders = parseTableHeaders(JSON.parse(result.data))
          props.tableRows = parseTableRows(JSON.parse(result.data))
          return JSON.parse(result.data)
      }).catch( function(result){
          //This is where you would put an error callback
      })


}

我將代碼的結構錯誤嗎? 我的理解是,當道具更改時,將強制重新渲染。 我應該將“ this.state.apiData”而不是整個狀態傳遞給apiCall嗎?

apiCall(this.state.apiData)

它在componentDidMount()中運行,我相信這是調用API的正確位置,但不會在回調時重新呈現。 我可以在調試器中看到狀態變量正在按預期更新。 我應該在apiCall()設置一個返回變量,並讓返回值更新componentDidMount()中的狀態嗎? 返回數據后是否會強制重新渲染?

這樣的東西?

this.setState({apiData: apiCall()})

如果我從apiCall()返回this.state.apiData並讓其解析apiCall中的表標題和行,則在返回狀態變量時,這將強制更新嗎?

您正在運行異步調用以獲取一些剩余的api數據。 按照定義,異步意味着您不知道代碼何時完成。 這意味着您需要在apiCall完成后運行某種類型的回調。

您在這里有一個rest api調用,該調用返回一個promise對象。 promise對象基本上是用於將回調添加到異步代碼的接口。 我建議您在restApi調用之后采用以下選項之一來運行回調。

1.)您可以將回調函數作為第二個參數傳遞到restApi()中。 您可以這樣調用此回調:

let that = this;
apiCall(props, function(result) {
    that.setState({apiData: result});
});

export function  apiCall(props, callback) {
    ...
    apigClient.invokeApi(params, pathTemplate, method, additionalParams)
        .then(function(result){
            ...
            callback(result);
            ...
        });
    ...
}

2)您的另一種選擇是通過鎖定api調用創建的promise處理apiCall的解析。 當您執行異步代碼時,對async方法的調用會立即返回promise對象,您可以將其返回到調用函數以允許調用方附加回調。 這聽起來可能有點令人困惑,但我並不是最擅長解釋事物,但請參閱以下內容:

let that = this;
apiCall(props).then(function(result) {
    that.setState({apiData: result});
});

export function  apiCall(props) {
    ...
    return apigClient.invokeApi(params, pathTemplate, method, additionalParams)
    .then(function(result){
        ...
    });
}

此處的主要區別是您將實際的異步調用返回給apigClient.invokeApi。 這樣,無論調用apiCall()的人如何,都可以在.then()方法中附加任何回調功能。

最終,您要確保在restApi數據實際返回給調用者時正在調用setState。 .then()是觸發該調用並可靠地獲取返回結果的最簡單方法。

注意:您還應該研究JS中的promise,因為.then方法可以接受2個參數,一個處理數據的成功返回,另一個處理錯誤報告。

埃里克·哈塞爾伯林(Eric Hasselbring)幫助我獲得了這個答案。 我正在調用apiCall(),並且未在componentDidMount內部返回任何內容。 我在methods.js apigClient.invokeAPI()之前添加了return,以便它將返回函數的結果

  return apigClient.invokeApi(params, pathTemplate, method, additionalParams)
      .then(function(result){
          //This is where you would put a success callback
          console.log('apiCall Returned. searchTerm; ', searchTerm)
          console.log(result)
          debugger
          props.tableHeaders = parseTableHeaders(JSON.parse(result.data))
          props.tableRows = parseTableRows(JSON.parse(result.data))
          return JSON.parse(result.data)
      }).catch( function(result){
          //This is where you would put an error callback
      })

然后我將SearchBody.js中的函數調用更新為

componentDidMount() {
  console.log('Inside compDidMount')
  if (this.state.urlSearchTerm){
      apiCall(this.state).then(apiResponse => this.setState({
        loaded: true,
        apiData: apiResponse
      })
    )
  }
 }

所以現在apiCall返回一個promise的結果,由於添加了.then函數,因此apiCall現在是一個promise。 這允許在componentDidMount內部更改狀態,以便React看到發生了道具更改並重新渲染。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM