[英]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.