Reputation:
OK, I am advancing my project from having simple data arrays in the component to an API call for the data, and as things always are in life, the component no longer works. The API is working I can see the data results, but it is just not producing them in the render:
// function
showMainTestimonial(clientId) {
let results = [];
let linkButtonNode = null;
TestimonyApi.getAll()
.then(function (data) {
var data = data.data;
if (data.showMore) {
linkButtonNode = (
<div style={{ margin:15, width:'100%', textAlign:'center' }}>
<Link className="button" to={ data.testimonialsLink }>More Testimonials</Link></div>)
}
data.testimonials.map(function (testimony, index) {
let commenter = null;
let category = null;
if (testimony.isMain && results.length === 0) {
if (data.showCommenterName) {
commenter = (
<div style={{ textAlign: 'right', fontSize: 16 }}>
<i>-- { testimony.commenter }</i>
</div>
);
}
if (testimony.category) {
category = (
<div style={{ textAlign: 'right', fontSize: 16 }}>
<i> { testimony.category }</i>
</div>
);
}
results.push(
<div id="TestimonialArea" key={ index }>
<div className="main" id="TestimonialZone">
<div id="TestimonialsFeed" className="NavFeed">
<div className="testspcr"></div>
<article className="lists">
<h3>
"{ testimony.title }"
</h3>
<div className="ReviewText">
"{ testimony.comment }"
</div>
{ commenter }
{ category }
</article>
{ linkButtonNode }
</div>
</div>
</div>
)
console.log('results from function: ' + JSON.stringify(results))
return results;
}
});
}.bind(this))
}
// render
render() {
let clientId = this.props.clientId;
var results = this.showMainTestimonial(clientId);
console.log('render results: ' + results);
return (
<section>
{ results }
</section>
)
}
As you can see the data is there, I am just doing something STUPID.
Any ideas?
Thanks in Advance.
Upvotes: 1
Views: 377
Reputation:
I figured out my problem, and refactoring the code makes it clearer to understand.
Basically as FakeRain said, the arrow function was key, although I guess I don't work well with general prose / high altitude theory. No matter, for anyone else struggling.
When you fetch data, and then plan to map it into a specific html 'fragment', split up the fetching and the mapping into 2 different functions, and remember to:
- Use the arrow function in the API fetching; (because of 'this')
- Call the mapping function in the API function; (because the API call is asynchronous)
- Bind the mapping function;
- Pass the results of your mapping to state.
So, illustratively speaking:
componentDidMount() {
this.getAPIData();
}
getAPIData() {
TestimonyApi.getAll()
.then((response) => { <-- don't forget the arrow function
let data = response.data;
// call the mapping function inside the API function
this.showMainTestimonial(data)
})
}
showMainTestimonial(data) {
let results = [];
data.map(function(item) {
// do something with mapped data and push in results
results.push (
// customizing the mapping into whatever results you want
)}.bind(this)) <--- don't forget to bind
// pass your configured html into state now after mapping
this.setState({results});
}
Upvotes: 0
Reputation: 817128
TestimonyApi.getAll
/ promises are asynchronous. This is a very common problem for people new to JavaScript and has been extensively discussed:
In the context of React, the data should probably be part of the component's state. Instead of returning results
(into the void), update the state instead:
this.setState({results});
This will cause the component to rerender. Inside the render
method, access this.state.results
instead.
You can start the fetching "automatically" by calling the method after the component was mounted. See componentDidMount
in Lifecycle Methods.
FYI, the render
method should never really fetch data. It is called after the data has changed (either because the component's props or state changed). Maybe reading Thinking in React will help you get a better understanding of this.
Upvotes: 1
Reputation: 86270
You need to take the result of the promise and put it in state, and then have render be based on state.
class Foo extends React.Component {
constructor(){
super();
this.state = {data: null};
}
fetchTestimonial(clientId) {
TestimonyApi.getAll()
.then((x) => this.setState({data: x}))
}
render(){
if (!this.state.data) return <div>Loading</div>
return (
<div>
{this.state.data.map(f)}
</div>
)
}
}
Note: the arrow function is important, it ensures this
is correct in the .then callback. Consider only using arrow functions inside methods because it avoids an entire type of bug.
Upvotes: 3