我无法弄清楚如何让API调用重新渲染到屏幕上。我有一个apiCall函数传递this.state并通过传递ref更改状态,但它不会触发对props更改的重新渲染。
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中的表头和行,当返回状态变量时会强制更新吗?
答案 0 :(得分:1)
您正在运行异步调用以获取其他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的解析。当您执行异步代码时,对异步方法的调用会立即返回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中的promises,因为.then方法可以接受2个参数,一个处理成功数据返回的函数和一个处理错误报告的函数。
答案 1 :(得分:0)
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中返回一个结果,而apiCall现在是一个承诺,因为添加了.then函数。这允许在componentDidMount中更改状态,以便反应看到有一个prop更改并重新渲染。